Skip to content

Commit

Permalink
Merge pull request #36 from flubshi/matrix_curl_redirect
Browse files Browse the repository at this point in the history
Improvement: Allow cookie handling for curl on redirects
  • Loading branch information
flubshi authored Oct 3, 2019
2 parents bbdd981 + 8b0de80 commit 38987c3
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 53 deletions.
3 changes: 2 additions & 1 deletion pvr.waipu/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.waipu"
version="1.2.0"
version="1.2.1"
name="waipu.tv PVR Client"
provider-name="flubshi">
<requires>@ADDON_DEPENDS@
Expand Down Expand Up @@ -29,6 +29,7 @@
<screenshot>resources/screenshots/screenshot-02.jpg</screenshot>
</assets>
<news>
- 1.2.1 Internal improvements to prepare for O2 authentication
- 1.1.0 Fix timers not displayed; Add option to install widevine
- 1.0.8 Update build sytem version; change header include way
- 1.0.7 Add workaround for O2 Accounts
Expand Down
177 changes: 127 additions & 50 deletions src/Curl.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* taken from pvr:zattoo
* originally taken from pvr.zattoo
*/
#include "Curl.h"

Expand All @@ -17,11 +17,29 @@ Curl::~Curl() = default;

string Curl::GetCookie(const string& name)
{
if (cookies.find(name) == cookies.end())
for (const auto& cookie : cookies)
{
return "";
if (cookie.name == name)
return cookie.value;
}
return cookies[name];
return "";
}

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

void Curl::AddHeader(const string& name, const string& value)
Expand Down Expand Up @@ -54,21 +72,58 @@ string Curl::Post(const string& url, const string& postData, int& statusCode)
return Request("POST", url, postData, statusCode);
}

string Curl::Request(const string& action,
const string& url,
const string& postData,
int& statusCode)
void Curl::ParseCookies(void* file, const string& host)
{
int numValues;
char** cookiesPtr = XBMC->GetFilePropertyValues(file, XFILE::FILE_PROPERTY_RESPONSE_HEADER,
"set-cookie", &numValues);
for (int i = 0; i < numValues; i++)
{
char* cookiePtr = cookiesPtr[i];
if (cookiePtr && *cookiePtr)
{
string cookie = cookiePtr;
std::string::size_type paramPos = cookie.find(';');
if (paramPos != std::string::npos)
cookie.resize(paramPos);
vector<string> parts = Utils::SplitString(cookie, '=', 2);
if (parts.size() != 2)
{
continue;
}
SetCookie(host, parts[0], parts[1]);
XBMC->Log(LOG_DEBUG, "Got cookie: %s.", parts[0].c_str());
}
}
XBMC->FreeStringArray(cookiesPtr, numValues);
}

string Curl::ParseHostname(const string& url)
{
size_t pos = url.find_first_of(":");
if (pos == string::npos)
return "";
string host = url.substr(pos + 3);

size_t pos_end = host.find_first_of("://");
if (pos_end == string::npos)
return host;
host = host.substr(0, pos_end);
return host;
}

void* Curl::PrepareRequest(const string& action, const string& url, const string& postData)
{
void* file = XBMC->CURLCreate(url.c_str());
if (!file)
{
statusCode = -1;
return "";
return nullptr;
}

XBMC->CURLAddOption(file, XFILE::CURL_OPTION_PROTOCOL, "redirect-limit", "0");
XBMC->CURLAddOption(file, XFILE::CURL_OPTION_PROTOCOL, "customrequest", action.c_str());

XBMC->CURLAddOption(file, XFILE::CURL_OPTION_HEADER, "acceptencoding", "gzip");

if (!postData.empty())
{
string base64 = Base64Encode((const unsigned char*)postData.c_str(), postData.size(), false);
Expand All @@ -86,58 +141,79 @@ string Curl::Request(const string& action,
entry.second.c_str());
}

string host = ParseHostname(url);
XBMC->Log(LOG_DEBUG, "Add cookies for host: %s.", host.c_str());
string cookie_s = "";
for (auto& cookie : cookies)
{
if (cookie.host != host)
continue;
cookie_s = cookie_s + cookie.name.c_str() + "=" + cookie.value.c_str() + "; ";
}
if (cookie_s.size() > 0)
XBMC->CURLAddOption(file, XFILE::CURL_OPTION_PROTOCOL, "cookie", cookie_s.c_str());

// we have to set "failonerror" to get error results
XBMC->CURLAddOption(file, XFILE::CURL_OPTION_HEADER, "failonerror", "false");
return file;
}

if (!XBMC->CURLOpen(file, XFILE::READ_NO_CACHE))
{
statusCode = -1;
return "";
}

statusCode = 200;
string Curl::Request(const string& action,
const string& url,
const string& postData,
int& statusCode)
{
int remaining_redirects = redirectLimit;
location = url;
bool redirect;
void* file = PrepareRequest(action, url, postData);

// get the real statusCode
char* tmpCode = XBMC->GetFilePropertyValue(file, XFILE::FILE_PROPERTY_RESPONSE_PROTOCOL, "");
std::string tmpRespLine;
tmpRespLine = tmpCode != nullptr ? tmpCode : "";
vector<string> resp_protocol_parts = Utils::SplitString(tmpRespLine, ' ', 3);
if (resp_protocol_parts.size() == 3)
do
{
statusCode = Utils::stoiDefault(resp_protocol_parts[1].c_str(), -1);
XBMC->Log(LOG_DEBUG, "HTTP response code: %i.", statusCode);
}
XBMC->FreeString(tmpCode);
redirect = false;
if (file == nullptr)
{
statusCode = -1;
return "";
}

int numValues;
char** cookiesPtr = XBMC->GetFilePropertyValues(file, XFILE::FILE_PROPERTY_RESPONSE_HEADER,
"set-cookie", &numValues);
if (!XBMC->CURLOpen(file, XFILE::READ_NO_CACHE))
{
statusCode = -1;
return "";
}

for (int i = 0; i < numValues; i++)
{
char* cookiePtr = cookiesPtr[i];
if (cookiePtr && *cookiePtr)
statusCode = 200;

// get the real statusCode
char* tmpCode = XBMC->GetFilePropertyValue(file, XFILE::FILE_PROPERTY_RESPONSE_PROTOCOL, "");
std::string tmpRespLine;
tmpRespLine = tmpCode != nullptr ? tmpCode : "";
vector<string> resp_protocol_parts = Utils::SplitString(tmpRespLine, ' ', 3);
if (resp_protocol_parts.size() >= 2)
{
string cookie = cookiePtr;
std::string::size_type paramPos = cookie.find(';');
if (paramPos != std::string::npos)
cookie.resize(paramPos);
vector<string> parts = Utils::SplitString(cookie, '=', 2);
if (parts.size() != 2)
{
continue;
}
cookies[parts[0]] = parts[1];
XBMC->Log(LOG_DEBUG, "Got cookie: %s.", parts[0].c_str());
statusCode = Utils::stoiDefault(resp_protocol_parts[1].c_str(), -1);
XBMC->Log(LOG_DEBUG, "HTTP response code: %i.", statusCode);
}
}
XBMC->FreeStringArray(cookiesPtr, numValues);
XBMC->FreeString(tmpCode);

char* tmp = XBMC->GetFilePropertyValue(file, XFILE::FILE_PROPERTY_RESPONSE_HEADER, "Location");
location = tmp != nullptr ? tmp : "";
ParseCookies(file, ParseHostname(location));

XBMC->FreeString(tmp);
char* tmp = XBMC->GetFilePropertyValue(file, XFILE::FILE_PROPERTY_RESPONSE_HEADER, "Location");
location = tmp != nullptr ? tmp : "";
XBMC->Log(LOG_DEBUG, "Location: %s.", location.c_str());
XBMC->FreeString(tmp);

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

// read the file
static const unsigned int CHUNKSIZE = 16384;
Expand All @@ -154,6 +230,7 @@ string Curl::Request(const string& action,
return body;
}


std::string Curl::Base64Encode(unsigned char const* in, unsigned int in_len, bool urlEncode)
{
std::string ret;
Expand Down
22 changes: 20 additions & 2 deletions src/Curl.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
/*
* taken from pvr:zattoo
* originally taken from pvr.zattoo
*/
#include <list>
#include <map>
#include <string>

struct Cookie
{
std::string host;
std::string name;
std::string value;
};

class Curl
{
public:
Expand All @@ -16,16 +24,26 @@ class Curl
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; }

private:
virtual void* PrepareRequest(const std::string& action,
const std::string& url,
const std::string& postData);
virtual void ParseCookies(void* 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::map<std::string, std::string> cookies;
std::list<Cookie> cookies;
std::string location;
int redirectLimit = 8;
};

0 comments on commit 38987c3

Please sign in to comment.