Skip to content

Commit

Permalink
Merge pull request #13 from ksooo/cleanup_and_fixes_02
Browse files Browse the repository at this point in the history
Matrix: Cleanup and Fixes
  • Loading branch information
ksooo authored Dec 17, 2021
2 parents abf3662 + ac3b07f commit 8845256
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 439 deletions.
2 changes: 1 addition & 1 deletion pvr.plutotv/addon.xml.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon
id="pvr.plutotv"
version="19.0.1"
version="19.0.2"
name="Pluto.tv PVR Client"
provider-name="Team Kodi, flubshi">
<requires>@ADDON_DEPENDS@
Expand Down
7 changes: 7 additions & 0 deletions pvr.plutotv/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
v19.0.2
- Fix memory leak when redirecting HTTP requests
- Load channel data on demand, not on add-on startup
- Do not report API call success when channels can't be loaded
- Fix crash on HTTP request error
- Code cleanup

v19.0.1
- Speedup and fix EPG data caching

Expand Down
163 changes: 87 additions & 76 deletions src/Curl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,58 @@
#include "Curl.h"

#include "Utils.h"
#include "kodi/tools/StringUtils.h"

#include <utility>
namespace
{
std::string Base64Encode(const std::string& str, bool urlEncode)
{
std::string ret;
int i = 3;
unsigned char c_3[3];
unsigned char c_4[4];

static const char* to_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int len = str.size();
int curr = 0;
while (len)
{
i = len > 2 ? 3 : len;
len -= i;
c_3[0] = str[curr++];
c_3[1] = i > 1 ? str[curr++] : 0;
c_3[2] = i > 2 ? str[curr++] : 0;

c_4[0] = (c_3[0] & 0xfc) >> 2;
c_4[1] = ((c_3[0] & 0x03) << 4) + ((c_3[1] & 0xf0) >> 4);
c_4[2] = ((c_3[1] & 0x0f) << 2) + ((c_3[2] & 0xc0) >> 6);
c_4[3] = c_3[2] & 0x3f;

for (int j = 0; j < i + 1; ++j)
{
if (urlEncode && to_base64[c_4[j]] == '+')
ret += "%2B";
else if (urlEncode && to_base64[c_4[j]] == '/')
ret += "%2F";
else
ret += to_base64[c_4[j]];
}
}
while ((i++ < 3))
ret += urlEncode ? "%3D" : "=";

return ret;
}
} // namespace

Curl::Curl() = default;

Curl::~Curl() = default;

std::string Curl::GetCookie(const std::string& name)
{
for (const auto& cookie : cookies)
for (const auto& cookie : m_cookies)
{
if (cookie.name == name)
return cookie.value;
Expand All @@ -29,34 +71,35 @@ std::string Curl::GetCookie(const std::string& name)

void Curl::SetCookie(const std::string& host, const std::string& name, const std::string& value)
{
for (std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); ++i)
for (auto& cookie : m_cookies)
{
if (i->host == host && i->name == name)
if (cookie.host == host && cookie.name == name)
{
i->value = value;
cookie.value = value;
return;
}
}

Cookie cookie;
cookie.host = host;
cookie.name = name;
cookie.value = value;
cookies.push_back(cookie);
m_cookies.emplace_back(cookie);
}

void Curl::AddHeader(const std::string& name, const std::string& value)
{
headers[name] = value;
m_headers[name] = value;
}

void Curl::AddOption(const std::string& name, const std::string& value)
{
options[name] = value;
m_options[name] = value;
}

void Curl::ResetHeaders()
{
headers.clear();
m_headers.clear();
}

std::string Curl::Delete(const std::string& url, const std::string& postData, int& statusCode)
Expand All @@ -76,34 +119,35 @@ std::string Curl::Post(const std::string& url, const std::string& postData, int&

void Curl::ParseCookies(kodi::vfs::CFile* file, const std::string& host)
{
int numValues;
const std::vector<std::string> cookies =
file->GetPropertyValues(ADDON_FILE_PROPERTY_RESPONSE_HEADER, "set-cookie");
for (auto cookie : cookies)
{
std::string::size_type paramPos = cookie.find(';');
const std::string::size_type paramPos = cookie.find(';');
if (paramPos != std::string::npos)
cookie.resize(paramPos);
std::vector<std::string> parts = Utils::SplitString(cookie, '=', 2);

const std::vector<std::string> parts = kodi::tools::StringUtils::Split(cookie, "=", 2);
if (parts.size() != 2)
{
continue;
}

SetCookie(host, parts[0], parts[1]);
kodi::Log(ADDON_LOG_DEBUG, "Got cookie: %s.", parts[0].c_str());
}
}

std::string Curl::ParseHostname(const std::string& url)
{
size_t pos = url.find_first_of(":");
const size_t pos = url.find_first_of(':');
if (pos == std::string::npos)
return "";

std::string host = url.substr(pos + 3);

size_t pos_end = host.find_first_of("://");
const size_t pos_end = host.find_first_of("://");
if (pos_end == std::string::npos)
return host;

host = host.substr(0, pos_end);
return host;
}
Expand All @@ -118,39 +162,40 @@ kodi::vfs::CFile* Curl::PrepareRequest(const std::string& action,
delete file;
return nullptr;
}

file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "redirect-limit", "0");
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "customrequest", action.c_str());
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "customrequest", action);

file->CURLAddOption(ADDON_CURL_OPTION_HEADER, "acceptencoding", "gzip");

if (!postData.empty())
{
std::string base64 =
Base64Encode((const unsigned char*)postData.c_str(), postData.size(), false);
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "postdata", base64.c_str());
const std::string base64 = Base64Encode(postData, false);
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "postdata", base64);
}

for (auto const& entry : headers)
for (auto const& entry : m_headers)
{
file->CURLAddOption(ADDON_CURL_OPTION_HEADER, entry.first.c_str(), entry.second.c_str());
file->CURLAddOption(ADDON_CURL_OPTION_HEADER, entry.first, entry.second);
}

for (auto const& entry : options)
for (auto const& entry : m_options)
{
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, entry.first.c_str(), entry.second.c_str());
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, entry.first, entry.second);
}

std::string host = ParseHostname(url);
const std::string host = ParseHostname(url);
kodi::Log(ADDON_LOG_DEBUG, "Add cookies for host: %s.", host.c_str());
std::string cookie_s = "";
for (auto& cookie : cookies)
std::string cookie_s;
for (auto& cookie : m_cookies)
{
if (cookie.host != host)
continue;
cookie_s = cookie_s + cookie.name.c_str() + "=" + cookie.value.c_str() + "; ";

cookie_s = cookie_s + cookie.name + "=" + cookie.value + "; ";
}
if (cookie_s.size() > 0)
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "cookie", cookie_s.c_str());
file->CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "cookie", cookie_s);

// we have to set "failonerror" to get error results
file->CURLAddOption(ADDON_CURL_OPTION_HEADER, "failonerror", "false");
Expand All @@ -163,8 +208,8 @@ std::string Curl::Request(const std::string& action,
const std::string& postData,
int& statusCode)
{
int remaining_redirects = redirectLimit;
location = url;
int remaining_redirects = m_redirectLimit;
m_location = url;
bool redirect;
kodi::vfs::CFile* file = PrepareRequest(action, url, postData);

Expand All @@ -186,26 +231,30 @@ std::string Curl::Request(const std::string& action,
statusCode = 200;

// get the real statusCode
std::string tmpRespLine = file->GetPropertyValue(ADDON_FILE_PROPERTY_RESPONSE_PROTOCOL, "");
std::vector<std::string> resp_protocol_parts = Utils::SplitString(tmpRespLine, ' ', 3);
const std::string tmpRespLine =
file->GetPropertyValue(ADDON_FILE_PROPERTY_RESPONSE_PROTOCOL, "");
const std::vector<std::string> resp_protocol_parts =
kodi::tools::StringUtils::Split(tmpRespLine, " ", 3);

if (resp_protocol_parts.size() >= 2)
{
statusCode = Utils::stoiDefault(resp_protocol_parts[1].c_str(), -1);
statusCode = Utils::StringToInt(resp_protocol_parts[1], -1);
kodi::Log(ADDON_LOG_DEBUG, "HTTP response code: %i.", statusCode);
}

ParseCookies(file, ParseHostname(location));
ParseCookies(file, ParseHostname(m_location));

location = file->GetPropertyValue(ADDON_FILE_PROPERTY_RESPONSE_HEADER, "Location");
kodi::Log(ADDON_LOG_DEBUG, "Location: %s.", location.c_str());
m_location = file->GetPropertyValue(ADDON_FILE_PROPERTY_RESPONSE_HEADER, "Location");
kodi::Log(ADDON_LOG_DEBUG, "Location: %s.", m_location.c_str());

if (statusCode >= 301 && statusCode <= 303)
{
// handle redirect
redirect = true;
kodi::Log(ADDON_LOG_DEBUG, "redirects remaining: %i", remaining_redirects);
remaining_redirects--;
file = PrepareRequest("GET", location.c_str(), "");
delete file;
file = PrepareRequest("GET", m_location, "");
}
} while (redirect && remaining_redirects >= 0);

Expand All @@ -223,41 +272,3 @@ std::string Curl::Request(const std::string& action,
delete file;
return body;
}


std::string Curl::Base64Encode(unsigned char const* in, unsigned int in_len, bool urlEncode)
{
std::string ret;
int i(3);
unsigned char c_3[3];
unsigned char c_4[4];

const char* to_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

while (in_len)
{
i = in_len > 2 ? 3 : in_len;
in_len -= i;
c_3[0] = *(in++);
c_3[1] = i > 1 ? *(in++) : 0;
c_3[2] = i > 2 ? *(in++) : 0;

c_4[0] = (c_3[0] & 0xfc) >> 2;
c_4[1] = ((c_3[0] & 0x03) << 4) + ((c_3[1] & 0xf0) >> 4);
c_4[2] = ((c_3[1] & 0x0f) << 2) + ((c_3[2] & 0xc0) >> 6);
c_4[3] = c_3[2] & 0x3f;

for (int j = 0; (j < i + 1); ++j)
{
if (urlEncode && to_base64[c_4[j]] == '+')
ret += "%2B";
else if (urlEncode && to_base64[c_4[j]] == '/')
ret += "%2F";
else
ret += to_base64[c_4[j]];
}
}
while ((i++ < 3))
ret += urlEncode ? "%3D" : "=";
return ret;
}
55 changes: 27 additions & 28 deletions src/Curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

#include "kodi/Filesystem.h"

#include <list>
#include <map>
#include <string>
#include <vector>

struct Cookie
{
Expand All @@ -25,33 +25,32 @@ class Curl
public:
Curl();
virtual ~Curl();
virtual std::string Delete(const std::string& url, const std::string& postData, int& statusCode);
virtual std::string Get(const std::string& url, int& statusCode);
virtual std::string Post(const std::string& url, const std::string& postData, int& statusCode);
virtual void AddHeader(const std::string& name, const std::string& value);
virtual void AddOption(const std::string& name, const std::string& value);
virtual void ResetHeaders();
virtual std::string GetCookie(const std::string& name);
virtual void SetCookie(const std::string& host,
const std::string& name,
const std::string& value);
virtual std::string GetLocation() { return location; }
virtual void SetRedirectLimit(int limit) { redirectLimit = limit; }

std::string Delete(const std::string& url, const std::string& postData, int& statusCode);
std::string Get(const std::string& url, int& statusCode);
std::string Post(const std::string& url, const std::string& postData, int& statusCode);
void AddHeader(const std::string& name, const std::string& value);
void AddOption(const std::string& name, const std::string& value);
void ResetHeaders();
std::string GetCookie(const std::string& name);
void SetCookie(const std::string& host, const std::string& name, const std::string& value);
std::string GetLocation() const { return m_location; }
void SetRedirectLimit(int limit) { m_redirectLimit = limit; }

private:
virtual kodi::vfs::CFile* PrepareRequest(const std::string& action,
const std::string& url,
const std::string& postData);
virtual void ParseCookies(kodi::vfs::CFile* file, const std::string& host);
virtual std::string Request(const std::string& action,
const std::string& url,
const std::string& postData,
int& statusCode);
virtual std::string ParseHostname(const std::string& url);
std::string Base64Encode(unsigned char const* in, unsigned int in_len, bool urlEncode);
std::map<std::string, std::string> headers;
std::map<std::string, std::string> options;
std::list<Cookie> cookies;
std::string location;
int redirectLimit = 8;
kodi::vfs::CFile* PrepareRequest(const std::string& action,
const std::string& url,
const std::string& postData);
void ParseCookies(kodi::vfs::CFile* file, const std::string& host);
std::string Request(const std::string& action,
const std::string& url,
const std::string& postData,
int& statusCode);
std::string ParseHostname(const std::string& url);

std::map<std::string, std::string> m_headers;
std::map<std::string, std::string> m_options;
std::vector<Cookie> m_cookies;
std::string m_location;
int m_redirectLimit = 8;
};
Loading

0 comments on commit 8845256

Please sign in to comment.