Add ability to download files from the homeserver

This commit is contained in:
rexy712 2019-10-17 15:18:03 -07:00
parent fc1885f9fe
commit fa75e8f0d7
9 changed files with 162 additions and 104 deletions

View File

@ -131,7 +131,24 @@ namespace matrix{
bool create_thumbnail(uploaded_image& info)const;
bool create_thumbnail(uploaded_video& video)const;
raii::binary download_file(const raii::string_base& url);
template<class DLHandler>
bool download_file(const raii::string_base& url, DLHandler&& dl){
_get_curl_setup(m_ses->urls.file_download(m_ses->homeserver, url));
m_curl.setwritefun(_download_dispatch<DLHandler>);
m_curl.setwritedata(&dl);
if(!_perform_curl())
return false;
return true;
}
template<class DLHandler>
void download_file(const raii::string_base& server, const raii::string_base& media_id, DLHandler&& dl);
private:
template<class DLHandler>
static size_t _download_dispatch(char* ptr, size_t size, size_t nmemb, void* userdata){
return reinterpret_cast<DLHandler*>(userdata)(ptr, size, nmemb, nullptr);
}
uploaded_file _upload_file(const file_details& file, const raii::curl_llist& header)const;
};
}

View File

@ -52,6 +52,7 @@ namespace matrix{
const raii::string& whoami(void)const;
const raii::string& displayname(void)const;
const raii::string& profile_picture(void)const;
raii::string file_download(const raii::string_base& homeserver, const raii::string_base& mediaid);
raii::string file_thumbnail(const raii::string_base& homeserver, const raii::string_base& fileurl, int width, int height, const raii::string_base& method)const;
raii::string logout(const raii::string_base& homeserver, const raii::string_base& access_token)const;
raii::string sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const;
@ -67,6 +68,7 @@ namespace matrix{
private:
void _initial_populate(const raii::string_base& homeserver);
static raii::string get_server_media_string(const raii::string_base& url);
static constexpr const char* s_proto = "https://";
static constexpr const char* s_revision = "r0";
};

View File

@ -66,13 +66,29 @@ namespace matrix{
long http_status(void)const;
protected:
struct put_data{
const char* data;
size_t len;
};
static size_t _reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
static size_t _binary_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
void _get_curl_setup(const raii::string_base& url)const;
void _post_curl_setup(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
static size_t _put_read_curl_callback(char* buffer, size_t size, size_t nmemb, void* userdata);
void _put_curl_setup(put_data& data, const raii::string_base& url, const raii::curl_llist& header)const;
protected:
using binary_callback_fun = size_t(*)(char*, size_t, size_t, void*);
using reply_callback_fun = size_t(*)(char*, size_t, size_t, void*);
void _set_curl_useragent(const raii::string_base& useragent);
raii::string _get_curl(const raii::string_base& url)const;
raii::binary _get_curl_binary(const raii::string_base& url)const;
raii::string _post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
raii::binary _post_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
raii::string _put_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
raii::binary _put_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
bool _perform_curl(void)const;
raii::string _get_curl(const raii::string_base& url, reply_callback_fun fun = _reply_curl_callback)const;
raii::binary _get_curl_binary(const raii::string_base& url, binary_callback_fun fun = _binary_reply_curl_callback)const;
raii::string _post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, reply_callback_fun = _reply_curl_callback)const;
raii::binary _post_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, binary_callback_fun = _binary_reply_curl_callback)const;
raii::string _put_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, reply_callback_fun = _reply_curl_callback)const;
raii::binary _put_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, binary_callback_fun = _binary_reply_curl_callback)const;
raii::rjp_string _post_and_find(const raii::string_base& data, const raii::string_base& url, const raii::curl_llist& header, const raii::string_base& target)const;
raii::rjp_string _get_and_find(const raii::string_base& url, const raii::string_base& search)const;
raii::rjp_string _curl_reply_search(const raii::string_base& reply, const raii::string_base& search)const;

View File

@ -39,7 +39,7 @@ namespace raii{
protected:
constexpr binary_base(void) = default;
constexpr binary_base(char* data, size_t size):
m_data(data), m_size(size){}
m_data(data), m_cap(size){}
template<class Allocator>
binary_base(const binary_base& b):
m_data(Allocator::copy(b.m_data, b.m_cap)),

View File

@ -31,6 +31,10 @@ namespace raii{
{
private:
CURL* m_curl;
public:
using write_function = size_t(*)(char*,size_t,size_t,void*);
using read_function = size_t(*)(char*,size_t,size_t,void*);
public:
curler(void);
curler(const curler& c);
@ -58,6 +62,10 @@ namespace raii{
curler& setpostdata(const char* s, curl_off_t len = -1);
curler& setpostdata(const string_base& s);
curler& forcessl(long version = CURL_SSLVERSION_DEFAULT);
curler& setwritefun(write_function);
curler& setwritedata(void*);
curler& setreadfun(read_function);
curler& setreaddata(void*);
void reset(void);
decltype(curl_easy_perform(m_curl)) perform(void);

View File

@ -156,6 +156,9 @@ namespace matrix{
bool client::create_thumbnail(uploaded_video& info)const{
return create_thumbnail(static_cast<uploaded_image&>(info));
}
raii::binary client::download_file(const raii::string_base& url){
return _get_curl_binary(m_ses->urls.file_download(m_ses->homeserver, url));
}
/*******************************
Internal functions
@ -175,13 +178,6 @@ namespace matrix{
return to_copy;
}
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
raii::string* data = reinterpret_cast<raii::string*>(userdata);
size_t oldlen = data->length();
data->resize(data->length() + (size*nmemb));
memcpy(data->get()+oldlen, ptr, size*nmemb);
return size*nmemb;
}
uploaded_file client::_upload_file(const file_details& file, const raii::curl_llist& header)const{
raii::string fileurl;
uploaded_file retval = {};
@ -194,7 +190,7 @@ namespace matrix{
m_curl.setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)upload_data.len);
m_curl.seturl(m_ses->urls.file_upload());
m_curl.setheader(header);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &fileurl);
CURLcode cres = m_curl.perform();
m_curl.setopt(CURLOPT_READDATA, NULL);

View File

@ -78,11 +78,14 @@ namespace matrix{
const raii::string& client_url_list::profile_picture(void)const{
return m_profile_picture;
}
raii::string client_url_list::file_download(const raii::string_base& homeserver, const raii::string_base& fileurl){
raii::string media = get_server_media_string(fileurl);
if(!media) return {};
return raii::string(s_proto + homeserver + "/_matrix/media/r0/download/" + media);
}
raii::string client_url_list::file_thumbnail(const raii::string_base& homeserver, const raii::string_base& fileurl, int width, int height, const raii::string_base& method)const{
if(strncmp(fileurl.get(), "mxc://", 6))
return {};
raii::string media(fileurl.get()+6, fileurl.length()-6);
raii::string media = get_server_media_string(fileurl);
if(!media) return {};
return raii::string(s_proto + homeserver + "/_matrix/media/r0/thumbnail/" + media + "?width=" + raii::itostr(width) + "&height=" + raii::itostr(height) + "&method=" + method);
}
raii::string client_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
@ -102,5 +105,11 @@ namespace matrix{
void client_url_list::_initial_populate(const raii::string_base& homeserver){
m_alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
}
raii::string client_url_list::get_server_media_string(const raii::string_base& url){
if(!url || strncmp(url.get(), "mxc://", 6))
return {};
return raii::string(url.get()+6, url.length()-6);
}
}

View File

@ -44,75 +44,29 @@ namespace matrix{
long connection::http_status(void)const{
return m_curl.last_status();
}
void connection::_set_curl_useragent(const raii::string_base& useragent){
m_curl.setuseragent(useragent);
}
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
size_t connection::_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
raii::string* data = reinterpret_cast<raii::string*>(userdata);
data->append(ptr, size*nmemb);
return size*nmemb;
}
static size_t _binary_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
size_t connection::_binary_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
raii::binary* data = reinterpret_cast<raii::binary*>(userdata);
data->append(ptr, size*nmemb);
return size*nmemb;
}
static void _get_curl_setup(const raii::string_base& url, raii::curler& curl){
curl.getreq();
curl.seturl(url);
curl.setheader(raii::curl_llist{});
void connection::_get_curl_setup(const raii::string_base& url)const{
m_curl.getreq();
m_curl.seturl(url);
m_curl.setheader(raii::curl_llist{});
}
raii::string connection::_get_curl(const raii::string_base& url)const{
raii::string reply;
_get_curl_setup(url, m_curl);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
CURLcode res = m_curl.perform();
if(res != CURLE_OK)
return {};
return reply;
void connection::_post_curl_setup(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
m_curl.postreq();
m_curl.setpostdata(postdata.get(), postdata.length());
m_curl.seturl(url);
m_curl.setheader(header);
}
raii::binary connection::_get_curl_binary(const raii::string_base& url)const{
raii::binary reply;
_get_curl_setup(url, m_curl);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _binary_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
CURLcode res = m_curl.perform();
if(res != CURLE_OK)
return {};
return reply;
}
static void _post_curl_setup(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, raii::curler& curl){
curl.postreq();
curl.setpostdata(postdata.get(), postdata.length());
curl.seturl(url);
curl.setheader(header);
}
raii::string connection::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
raii::string reply;
_post_curl_setup(postdata, url, header, m_curl);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
CURLcode res = m_curl.perform();
if(res != CURLE_OK)
return {};
return reply;
}
raii::binary connection::_post_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
raii::binary reply;
_post_curl_setup(postdata, url, header, m_curl);
m_curl.setopt(CURLOPT_WRITEFUNCTION, _binary_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
CURLcode res = m_curl.perform();
if(res != CURLE_OK)
return {};
return reply;
}
struct put_data{
const char* data;
size_t len;
};
static size_t _put_read_curl_callback(char* buffer, size_t size, size_t nmemb, void* userdata){
size_t connection::_put_read_curl_callback(char* buffer, size_t size, size_t nmemb, void* userdata){
put_data* src = reinterpret_cast<put_data*>(userdata);
size_t curl_size = size*nmemb;
size_t to_copy = std::min(curl_size, src->len);
@ -121,39 +75,83 @@ namespace matrix{
src->data += to_copy;
return to_copy;
}
static void _put_curl_setup(put_data& data, const raii::string_base& url, const raii::curl_llist& header, raii::curler& curl){
curl.putreq();
curl.setopt(CURLOPT_POSTFIELDS, data.data);
curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)data.len);
curl.seturl(url);
curl.setheader(header);
curl.setopt(CURLOPT_READFUNCTION, _put_read_curl_callback);
curl.setopt(CURLOPT_READDATA, &data);
curl.setopt(CURLOPT_INFILESIZE, (curl_off_t)data.len);
void connection::_put_curl_setup(put_data& data, const raii::string_base& url, const raii::curl_llist& header)const{
m_curl.putreq();
m_curl.setopt(CURLOPT_POSTFIELDS, data.data);
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)data.len);
m_curl.seturl(url);
m_curl.setheader(header);
m_curl.setreadfun(_put_read_curl_callback);
m_curl.setreaddata(&data);
m_curl.setopt(CURLOPT_INFILESIZE, (curl_off_t)data.len);
}
raii::string connection::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
raii::string reply;
put_data data{putdata.get(), putdata.length()};
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
_put_curl_setup(data, url, header, m_curl);
void connection::_set_curl_useragent(const raii::string_base& useragent){
m_curl.setuseragent(useragent);
}
bool connection::_perform_curl(void)const{
CURLcode res = m_curl.perform();
m_curl.setopt(CURLOPT_READDATA, NULL);
m_curl.setopt(CURLOPT_READFUNCTION, NULL);
if(res != CURLE_OK)
return (res == CURLE_OK);
}
raii::string connection::_get_curl(const raii::string_base& url, reply_callback_fun fun)const{
raii::string reply;
_get_curl_setup(url);
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
if(!_perform_curl())
return {};
return reply;
}
raii::binary connection::_put_curl_binary(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
raii::binary connection::_get_curl_binary(const raii::string_base& url, binary_callback_fun fun)const{
raii::binary reply;
_get_curl_setup(url);
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
if(!_perform_curl())
return {};
return reply;
}
raii::string connection::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, reply_callback_fun fun)const{
raii::string reply;
_post_curl_setup(postdata, url, header);
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
if(!_perform_curl())
return {};
return reply;
}
raii::binary connection::_post_curl_binary(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header, binary_callback_fun fun)const{
raii::binary reply;
_post_curl_setup(postdata, url, header);
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
if(!_perform_curl())
return {};
return reply;
}
raii::string connection::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header, reply_callback_fun fun)const{
raii::string reply;
put_data data{putdata.get(), putdata.length()};
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
_put_curl_setup(data, url, header);
bool succ = _perform_curl();
m_curl.setreaddata(NULL);
m_curl.setreadfun(NULL);
if(!succ)
return {};
return reply;
}
raii::binary connection::_put_curl_binary(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header, binary_callback_fun fun)const{
raii::binary reply;
put_data data{putdata.get(), putdata.length()};
m_curl.setopt(CURLOPT_WRITEFUNCTION, _binary_reply_curl_callback);
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
_put_curl_setup(data, url, header, m_curl);
CURLcode res = m_curl.perform();
m_curl.setopt(CURLOPT_READDATA, NULL);
m_curl.setopt(CURLOPT_READFUNCTION, NULL);
if(res != CURLE_OK)
m_curl.setwritefun(fun);
m_curl.setwritedata(&reply);
_put_curl_setup(data, url, header);
bool succ = _perform_curl();
m_curl.setreaddata(NULL);
m_curl.setreadfun(NULL);
if(!succ)
return {};
return reply;
}

View File

@ -92,6 +92,18 @@ namespace raii{
setopt(CURLOPT_SSLVERSION, version);
return *this;
}
curler& curler::setwritefun(write_function w){
return setopt(CURLOPT_WRITEFUNCTION, w);
}
curler& curler::setwritedata(void* data){
return setopt(CURLOPT_WRITEDATA, data);
}
curler& curler::setreadfun(read_function r){
return setopt(CURLOPT_READFUNCTION, r);
}
curler& curler::setreaddata(void* data){
return setopt(CURLOPT_READDATA, data);
}
void curler::reset(void){
curl_easy_reset(m_curl);