2011-01-18 04:41:54 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2010, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libtorrent/rss.hpp"
|
|
|
|
#include "libtorrent/xml_parse.hpp"
|
|
|
|
#include "libtorrent/http_parser.hpp"
|
|
|
|
#include "libtorrent/http_connection.hpp"
|
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
|
|
|
#include "libtorrent/session.hpp"
|
|
|
|
#include "libtorrent/settings.hpp"
|
|
|
|
#include "libtorrent/alert_types.hpp" // for rss_alert
|
|
|
|
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
|
|
|
|
namespace libtorrent {
|
|
|
|
|
|
|
|
struct feed_state
|
|
|
|
{
|
|
|
|
feed_state(feed& r)
|
|
|
|
: in_item(false)
|
|
|
|
, type(none)
|
|
|
|
, ret(r)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool in_item;
|
|
|
|
std::string current_tag;
|
|
|
|
enum feed_type
|
|
|
|
{
|
|
|
|
none, atom, rss2
|
|
|
|
} type;
|
|
|
|
feed_item current_item;
|
|
|
|
feed& ret;
|
|
|
|
|
|
|
|
bool is_item(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom: return string_equal_no_case(tag, "entry");
|
|
|
|
case rss2: return string_equal_no_case(tag, "item");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_title(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom:
|
|
|
|
case rss2: return string_equal_no_case(tag, "title");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_url(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom:
|
|
|
|
case rss2: return string_equal_no_case(tag, "link");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_desc(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom: return string_equal_no_case(tag, "summary");
|
|
|
|
case rss2: return string_equal_no_case(tag, "description")
|
|
|
|
|| string_equal_no_case(tag, "media:text");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_uuid(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom: return string_equal_no_case(tag, "id");
|
|
|
|
case rss2: return string_equal_no_case(tag, "guid");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_comment(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom: return false;
|
|
|
|
case rss2: return string_equal_no_case(tag, "comments");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_category(char const* tag) const
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case atom: return false;
|
|
|
|
case rss2: return string_equal_no_case(tag, "category");
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_size(char const* tag) const
|
|
|
|
{
|
|
|
|
return string_equal_no_case(tag, "size");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_hash(char const* tag) const
|
|
|
|
{
|
|
|
|
return string_equal_no_case(tag, "hash")
|
|
|
|
|| string_equal_no_case(tag, "media:hash");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_ttl(char const* tag) const
|
|
|
|
{
|
|
|
|
return string_equal_no_case(tag, "ttl");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void parse_feed(feed_state& f, int token, char const* name, char const* val)
|
|
|
|
{
|
|
|
|
switch (token)
|
|
|
|
{
|
|
|
|
case xml_parse_error:
|
|
|
|
f.ret.m_error = errors::parse_failed;
|
|
|
|
return;
|
|
|
|
case xml_start_tag:
|
|
|
|
case xml_empty_tag:
|
|
|
|
{
|
|
|
|
f.current_tag = name;
|
|
|
|
if (f.type == feed_state::none)
|
|
|
|
{
|
|
|
|
if (string_equal_no_case(f.current_tag.c_str(), "feed"))
|
|
|
|
f.type = feed_state::atom;
|
|
|
|
else if (string_equal_no_case(f.current_tag.c_str(), "rss"))
|
|
|
|
f.type = feed_state::rss2;
|
|
|
|
}
|
|
|
|
if (f.is_item(name)) f.in_item = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case xml_attribute:
|
|
|
|
{
|
|
|
|
if (!f.in_item) return;
|
|
|
|
if (f.is_url(f.current_tag.c_str())
|
|
|
|
&& f.type == feed_state::atom)
|
|
|
|
{
|
|
|
|
// atom feeds have items like this:
|
|
|
|
// <link href="http://..." length="12345"/>
|
|
|
|
if (string_equal_no_case(name, "href"))
|
|
|
|
f.current_item.url = val;
|
|
|
|
else if (string_equal_no_case(name, "length"))
|
|
|
|
f.current_item.size = strtoll(val, 0, 10);
|
|
|
|
}
|
|
|
|
else if (f.type == feed_state::rss2
|
|
|
|
&& string_equal_no_case(f.current_tag.c_str(), "enclosure"))
|
|
|
|
{
|
|
|
|
// rss feeds have items like this:
|
|
|
|
// <enclosure url="http://..." length="12345"/>
|
|
|
|
if (string_equal_no_case(name, "url"))
|
|
|
|
f.current_item.url = val;
|
|
|
|
else if (string_equal_no_case(name, "length"))
|
|
|
|
f.current_item.size = strtoll(val, 0, 10);
|
|
|
|
}
|
|
|
|
else if (f.type == feed_state::rss2
|
|
|
|
&& string_equal_no_case(f.current_tag.c_str(), "media:content"))
|
|
|
|
{
|
|
|
|
// rss feeds sometimes have items like this:
|
|
|
|
// <media:content url="http://..." filesize="12345"/>
|
|
|
|
if (string_equal_no_case(name, "url"))
|
|
|
|
f.current_item.url = val;
|
|
|
|
else if (string_equal_no_case(name, "filesize"))
|
|
|
|
f.current_item.size = strtoll(val, 0, 10);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case xml_end_tag:
|
|
|
|
{
|
|
|
|
if (f.in_item && f.is_item(name))
|
|
|
|
{
|
|
|
|
f.in_item = false;
|
|
|
|
if (!f.current_item.title.empty()
|
|
|
|
&& !f.current_item.url.empty())
|
|
|
|
f.ret.m_items.push_back(f.current_item);
|
|
|
|
f.current_item = feed_item();
|
|
|
|
}
|
|
|
|
f.current_tag = "";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case xml_string:
|
|
|
|
{
|
|
|
|
if (!f.in_item)
|
|
|
|
{
|
|
|
|
if (f.is_title(f.current_tag.c_str()))
|
|
|
|
f.ret.m_title = name;
|
|
|
|
else if (f.is_desc(f.current_tag.c_str()))
|
|
|
|
f.ret.m_description = name;
|
|
|
|
else if (f.is_ttl(f.current_tag.c_str()))
|
|
|
|
{
|
|
|
|
int tmp = atoi(name);
|
|
|
|
if (tmp > 0) f.ret.m_ttl = tmp;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (f.is_title(f.current_tag.c_str()))
|
|
|
|
f.current_item.title = name;
|
|
|
|
else if (f.is_desc(f.current_tag.c_str()))
|
|
|
|
f.current_item.description = name;
|
|
|
|
else if (f.is_uuid(f.current_tag.c_str()))
|
|
|
|
f.current_item.uuid = name;
|
|
|
|
else if (f.is_url(f.current_tag.c_str()) && f.type != feed_state::atom)
|
|
|
|
f.current_item.url = name;
|
|
|
|
else if (f.is_comment(f.current_tag.c_str()))
|
|
|
|
f.current_item.comment = name;
|
|
|
|
else if (f.is_category(f.current_tag.c_str()))
|
|
|
|
f.current_item.category = name;
|
|
|
|
else if (f.is_size(f.current_tag.c_str()))
|
|
|
|
f.current_item.size = strtoll(name, 0, 10);
|
|
|
|
else if (f.is_hash(f.current_tag.c_str()) && strlen(name) == 40)
|
|
|
|
{
|
|
|
|
if (!from_hex(name, 40, (char*)&f.current_item.info_hash[0]))
|
|
|
|
{
|
|
|
|
// hex parsing failed
|
|
|
|
f.current_item.info_hash.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case xml_declaration_tag: return;
|
|
|
|
case xml_comment: return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
torrent_handle add_feed_item(session& s, feed_item const& fi
|
|
|
|
, add_torrent_params const& tp, error_code& ec)
|
|
|
|
{
|
|
|
|
add_torrent_params p = tp;
|
|
|
|
p.url = fi.url;
|
|
|
|
p.uuid = fi.uuid;
|
|
|
|
// #error figure out how to get the feed url in here
|
|
|
|
// p.source_feed_url = ???;
|
|
|
|
p.ti.reset();
|
|
|
|
p.info_hash.clear();
|
|
|
|
p.name = fi.title.c_str();
|
|
|
|
return s.add_torrent(p, ec);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
|
|
torrent_handle add_feed_item(session& s, feed_item const& fi
|
|
|
|
, add_torrent_params const& tp)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
torrent_handle ret = add_feed_item(s, fi, tp, ec);
|
|
|
|
if (ec) throw libtorrent_exception(ec);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
boost::shared_ptr<feed> new_feed(aux::session_impl& ses, feed_settings const& sett)
|
|
|
|
{
|
|
|
|
return boost::shared_ptr<feed>(new feed(ses, sett));
|
|
|
|
}
|
|
|
|
|
|
|
|
feed::feed(aux::session_impl& ses, feed_settings const& sett)
|
|
|
|
: m_last_attempt(0)
|
|
|
|
, m_last_update(0)
|
|
|
|
, m_ttl(-1)
|
|
|
|
, m_updating(false)
|
|
|
|
, m_settings(sett)
|
|
|
|
, m_ses(ses)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::set_settings(feed_settings const& s)
|
|
|
|
{
|
|
|
|
m_settings = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::get_settings(feed_settings* s) const
|
|
|
|
{
|
|
|
|
*s = m_settings;
|
|
|
|
}
|
|
|
|
|
|
|
|
feed_handle feed::my_handle()
|
|
|
|
{
|
|
|
|
return feed_handle(boost::weak_ptr<feed>(shared_from_this()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::on_feed(error_code const& ec
|
|
|
|
, http_parser const& parser, char const* data, int size)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_updating);
|
|
|
|
m_updating = false;
|
|
|
|
|
|
|
|
if (ec && ec != asio::error::eof)
|
|
|
|
{
|
|
|
|
m_error = ec;
|
|
|
|
if (m_ses.m_alerts.should_post<rss_alert>())
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url
|
|
|
|
, rss_alert::state_error, m_error));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parser.status_code() != 200)
|
|
|
|
{
|
|
|
|
m_error = error_code(parser.status_code(), get_http_category());
|
|
|
|
if (m_ses.m_alerts.should_post<rss_alert>())
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url
|
|
|
|
, rss_alert::state_error, m_error));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* buf = const_cast<char*>(data);
|
|
|
|
|
|
|
|
feed_state s(*this);
|
|
|
|
xml_parse(buf, buf + size, boost::bind(&parse_feed, boost::ref(s), _1, _2, _3));
|
|
|
|
|
|
|
|
for (std::vector<feed_item>::iterator i = m_items.begin()
|
|
|
|
, end(m_items.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
i->handle = torrent_handle(m_ses.find_torrent(i->uuid.empty() ? i->url : i->uuid));
|
|
|
|
|
|
|
|
// if we're already downloading this torrent, or if we
|
|
|
|
// don't have auto-download enabled, just move along to
|
|
|
|
// the next one
|
|
|
|
if (i->handle.is_valid() || !m_settings.auto_download) continue;
|
|
|
|
|
|
|
|
// this means we should add this torrent to the session
|
|
|
|
add_torrent_params p = m_settings.add_args;
|
|
|
|
p.url = i->url;
|
|
|
|
p.uuid = i->uuid;
|
|
|
|
p.source_feed_url = m_settings.url;
|
|
|
|
p.ti.reset();
|
|
|
|
p.info_hash.clear();
|
|
|
|
p.name = i->title.c_str();
|
2011-02-24 05:25:35 +01:00
|
|
|
|
|
|
|
error_code e;
|
2011-01-18 04:41:54 +01:00
|
|
|
// #error session_impl::add_torrent doesn't support magnet links via url
|
2011-02-24 05:25:35 +01:00
|
|
|
m_ses.add_torrent(p, e);
|
2011-01-18 04:41:54 +01:00
|
|
|
|
2011-02-24 05:25:35 +01:00
|
|
|
if (e)
|
2011-01-18 04:41:54 +01:00
|
|
|
{
|
|
|
|
// #error alert!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_last_update = time(0);
|
|
|
|
|
|
|
|
// report that we successfully updated the feed
|
|
|
|
if (m_ses.m_alerts.should_post<rss_alert>())
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url
|
|
|
|
, rss_alert::state_updated, error_code()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// update m_ses.m_next_rss_update timestamps
|
|
|
|
// now that we have updated our timestamp
|
|
|
|
m_ses.update_rss_feeds();
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TORRENT_SETTING(t, x) {#x, offsetof(feed_settings,x), t},
|
|
|
|
bencode_map_entry feed_settings_map[] =
|
|
|
|
{
|
|
|
|
TORRENT_SETTING(std_string, url)
|
|
|
|
TORRENT_SETTING(boolean, auto_download)
|
|
|
|
TORRENT_SETTING(integer, default_ttl)
|
|
|
|
};
|
|
|
|
#undef TORRENT_SETTING
|
|
|
|
|
|
|
|
#define TORRENT_SETTING(t, x) {#x, offsetof(feed_item,x), t},
|
|
|
|
bencode_map_entry feed_item_map[] =
|
|
|
|
{
|
|
|
|
TORRENT_SETTING(std_string, url)
|
|
|
|
TORRENT_SETTING(std_string, uuid)
|
|
|
|
TORRENT_SETTING(std_string, title)
|
|
|
|
TORRENT_SETTING(std_string, description)
|
|
|
|
TORRENT_SETTING(std_string, comment)
|
|
|
|
TORRENT_SETTING(std_string, category)
|
|
|
|
TORRENT_SETTING(size_integer, size)
|
|
|
|
};
|
|
|
|
#undef TORRENT_SETTING
|
|
|
|
|
|
|
|
#define TORRENT_SETTING(t, x) {#x, offsetof(feed,x), t},
|
|
|
|
bencode_map_entry feed_map[] =
|
|
|
|
{
|
|
|
|
TORRENT_SETTING(std_string, m_title)
|
|
|
|
TORRENT_SETTING(std_string, m_description)
|
|
|
|
TORRENT_SETTING(time_integer, m_last_attempt)
|
|
|
|
TORRENT_SETTING(time_integer, m_last_update)
|
|
|
|
};
|
|
|
|
#undef TORRENT_SETTING
|
|
|
|
|
|
|
|
#define TORRENT_SETTING(t, x) {#x, offsetof(add_torrent_params,x), t},
|
|
|
|
bencode_map_entry add_torrent_map[] =
|
|
|
|
{
|
|
|
|
TORRENT_SETTING(std_string, save_path)
|
|
|
|
TORRENT_SETTING(boolean, paused)
|
|
|
|
TORRENT_SETTING(boolean, auto_managed)
|
|
|
|
TORRENT_SETTING(boolean, duplicate_is_error)
|
|
|
|
TORRENT_SETTING(boolean, seed_mode)
|
|
|
|
TORRENT_SETTING(boolean, override_resume_data)
|
|
|
|
TORRENT_SETTING(boolean, upload_mode)
|
|
|
|
TORRENT_SETTING(boolean, share_mode)
|
|
|
|
TORRENT_SETTING(std_string, trackerid)
|
|
|
|
};
|
|
|
|
#undef TORRENT_SETTING
|
|
|
|
|
|
|
|
void feed::load_state(lazy_entry const& rd)
|
|
|
|
{
|
|
|
|
load_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0]));
|
|
|
|
lazy_entry const* e = rd.dict_find_list("items");
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
m_items.reserve(e->list_size());
|
|
|
|
for (int i = 0; i < e->list_size(); ++i)
|
|
|
|
{
|
|
|
|
if (e->list_at(i)->type() != lazy_entry::dict_t) continue;
|
|
|
|
m_items.push_back(feed_item());
|
|
|
|
load_struct(*e->list_at(i), &m_items.back(), feed_item_map
|
|
|
|
, sizeof(feed_item_map)/sizeof(feed_item_map[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
load_struct(rd, &m_settings, feed_settings_map
|
|
|
|
, sizeof(feed_settings_map)/sizeof(feed_settings_map[0]));
|
|
|
|
e = rd.dict_find_dict("add_params");
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
load_struct(*e, &m_settings.add_args, add_torrent_map
|
|
|
|
, sizeof(add_torrent_map)/sizeof(add_torrent_map[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::save_state(entry& rd) const
|
|
|
|
{
|
|
|
|
save_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0]));
|
|
|
|
entry::list_type& items = rd["items"].list();
|
|
|
|
for (std::vector<feed_item>::const_iterator i = m_items.begin()
|
|
|
|
, end(m_items.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
items.push_back(entry());
|
|
|
|
entry& item = items.back();
|
|
|
|
save_struct(item, &*i, feed_item_map, sizeof(feed_item_map)/sizeof(feed_item_map[0]));
|
|
|
|
}
|
|
|
|
feed_settings sett_def;
|
|
|
|
save_struct(rd, &m_settings, feed_settings_map
|
|
|
|
, sizeof(feed_settings_map)/sizeof(feed_settings_map[0]), &sett_def);
|
|
|
|
entry& add = rd["add_params"];
|
|
|
|
add_torrent_params add_def;
|
|
|
|
save_struct(add, &m_settings.add_args, add_torrent_map
|
|
|
|
, sizeof(add_torrent_map)/sizeof(add_torrent_map[0]), &add_def);
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::update_feed()
|
|
|
|
{
|
|
|
|
if (m_updating) return;
|
|
|
|
|
|
|
|
m_last_attempt = time(0);
|
|
|
|
|
|
|
|
if (m_ses.m_alerts.should_post<rss_alert>())
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url
|
|
|
|
, rss_alert::state_updating, error_code()));
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<http_connection> feed(
|
|
|
|
new http_connection(m_ses.m_io_service, m_ses.m_half_open
|
|
|
|
, boost::bind(&feed::on_feed, shared_from_this()
|
|
|
|
, _1, _2, _3, _4)));
|
|
|
|
|
|
|
|
m_updating = true;
|
|
|
|
feed->get(m_settings.url, seconds(30), 0, 0, 5, m_ses.m_settings.user_agent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed::get_feed_status(feed_status* ret) const
|
|
|
|
{
|
|
|
|
ret->items = m_items;
|
|
|
|
ret->last_update = m_last_update;
|
|
|
|
ret->updating = m_updating;
|
|
|
|
ret->url = m_settings.url;
|
|
|
|
ret->title = m_title;
|
|
|
|
ret->description = m_description;
|
|
|
|
ret->error = m_error;
|
|
|
|
ret->ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl;
|
|
|
|
ret->next_update = next_update(time(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
int feed::next_update(time_t now) const
|
|
|
|
{
|
2011-02-21 06:24:41 +01:00
|
|
|
if (m_last_update == 0) return INT_MAX;
|
2011-01-18 04:41:54 +01:00
|
|
|
int ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl;
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT((m_last_update + ttl * 60) - now < INT_MAX);
|
|
|
|
return int((m_last_update + ttl * 60) - now);
|
2011-01-18 04:41:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// defined in session.cpp
|
|
|
|
void fun_wrap(bool* done, condition* e, mutex* m, boost::function<void(void)> f);
|
|
|
|
|
|
|
|
#define TORRENT_ASYNC_CALL(x) \
|
|
|
|
boost::shared_ptr<feed> f = m_feed_ptr.lock(); \
|
|
|
|
if (!f) return; \
|
|
|
|
aux::session_impl& ses = f->session(); \
|
|
|
|
ses.m_io_service.post(boost::bind(&feed:: x, f))
|
|
|
|
|
|
|
|
#define TORRENT_ASYNC_CALL1(x, a1) \
|
|
|
|
boost::shared_ptr<feed> f = m_feed_ptr.lock(); \
|
|
|
|
if (!f) return; \
|
|
|
|
aux::session_impl& ses = f->session(); \
|
|
|
|
ses.m_io_service.post(boost::bind(&feed:: x, f, a1))
|
|
|
|
|
|
|
|
#define TORRENT_SYNC_CALL1(x, a1) \
|
|
|
|
boost::shared_ptr<feed> f = m_feed_ptr.lock(); \
|
|
|
|
if (f) { \
|
|
|
|
bool done = false; \
|
|
|
|
aux::session_impl& ses = f->session(); \
|
|
|
|
mutex::scoped_lock l(ses.mut); \
|
|
|
|
ses.m_io_service.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function<void(void)>(boost::bind(&feed:: x, f, a1)))); \
|
|
|
|
f.reset(); \
|
|
|
|
do { ses.cond.wait(l); } while(!done); }
|
|
|
|
|
|
|
|
feed_handle::feed_handle(boost::weak_ptr<feed> const& p)
|
|
|
|
: m_feed_ptr(p) {}
|
|
|
|
|
|
|
|
void feed_handle::update_feed()
|
|
|
|
{
|
|
|
|
TORRENT_ASYNC_CALL(update_feed);
|
|
|
|
}
|
|
|
|
|
|
|
|
feed_status feed_handle::get_feed_status() const
|
|
|
|
{
|
|
|
|
feed_status ret;
|
|
|
|
TORRENT_SYNC_CALL1(get_feed_status, &ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void feed_handle::set_settings(feed_settings const& s)
|
|
|
|
{
|
|
|
|
TORRENT_SYNC_CALL1(set_settings, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
feed_settings feed_handle::settings() const
|
|
|
|
{
|
|
|
|
feed_settings ret;
|
|
|
|
TORRENT_SYNC_CALL1(get_settings, &ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|