diff --git a/ChangeLog b/ChangeLog index a3d3140a1541390a4748dd15db35de3bc6f01007..be512caaad8f172431c033f5e68e6334313aca84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,16 @@ +2004-08-05 <jaris@ifi.uio.no> + + * client: Added support for http urls. + + * delegator: Fixed a bug that caused us to request pieces the peer didn't have. (DOH!) + 2004-08-04 <jaris@ifi.uio.no> * bitfield: Clean up bitfield class and opimized .notIn(). * torrent: Finished http class, testing remains. -2004-08-03 Rakshasa <jaris@student.matnat.uio.no> +2004-08-03 <jaris@ifi.uio.no> * torrent: Don't throw on zero length piece messages. Why do they send these? diff --git a/TODO b/TODO index ac7256ed926c96604379cfb3d385d0477e434b61..372b6fd2ec747569988b37ad2ee3c54216614f96 100644 --- a/TODO +++ b/TODO @@ -42,6 +42,10 @@ Frobnicate the tracker reconnect timeout. Add host lookup+connection to SocketBase +Delegator doesn't need to copy the bitfield, do the notIn algorithm as you iterate. + +HttpGet should handle urls with only domain name (+port), without the leading '/'. + IN/AFTER API REDESIGN: diff --git a/client/Makefile b/client/Makefile index c66041ecb256e616c5cb92281efdfbcd15f745db..0ab386090fa6dd52dfc4edaf063c2e3620476f48 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -OBJECTS = display.o download.o rtorrent.o +OBJECTS = http.o display.o download.o rtorrent.o CFLAGS = -Wall -O3 -g -I.. LIBS = -L../torrent/.libs -ltorrent -lncurses -lcrypto diff --git a/client/README b/client/README index 7f6eeced0b3ad6e0656d2697119cc0bc5f32394c..afe11d7372890ede3c3ef93c400b34c86f3c076a 100644 --- a/client/README +++ b/client/README @@ -11,6 +11,12 @@ If your system doesn't have "execinfo.h", uncomment "#define USE_EXECINFO" in rtorrent.cc. This will be handled by an autoconf script later. +Files that start with "http://" are downloaded. The "Http:" counter on +the bottom of the main page shows how many downloads are in +progress. Check the logs 'l' to see if the download of a torrent +failed. You can also add torrents with the Enter key. + + All: Ctrl-C - Quit @@ -30,6 +36,7 @@ Main: Up/Down - Select a download Right - View download + Enter - Add torrent url L - View Log diff --git a/client/display.cc b/client/display.cc index 58bc4bcab888235320c939aae799fb8edc3bdaf8..b6192b63923ce913918ef0e01fc81f433b2c3d51 100644 --- a/client/display.cc +++ b/client/display.cc @@ -1,8 +1,12 @@ #include "display.h" +#include "http.h" #include <ncurses.h> int loops = 0; +extern bool inputActive; +extern std::string inputBuf; + Display::Display() { initscr(); cbreak(); @@ -30,9 +34,12 @@ void Display::drawDownloads(torrent::DList::const_iterator mark) { unsigned int fit = (maxY - 2) / 3; - mvprintw(0, std::max(0, (maxX - (signed)torrent::get(torrent::LIBRARY_NAME).size()) / 2 - 4), - "*** %s ***", - torrent::get(torrent::LIBRARY_NAME).c_str()); + if (!inputActive) + mvprintw(0, std::max(0, (maxX - (signed)torrent::get(torrent::LIBRARY_NAME).size()) / 2 - 4), + "*** %s ***", + torrent::get(torrent::LIBRARY_NAME).c_str()); + else + mvprintw(0, 0, "Input: %s", inputBuf.c_str()); torrent::DList::const_iterator first = torrent::downloads().begin(); torrent::DList::const_iterator last = torrent::downloads().end(); @@ -85,11 +92,11 @@ void Display::drawDownloads(torrent::DList::const_iterator mark) { torrent::get(first, torrent::TRACKER_MSG).c_str()); } - mvprintw(maxY - 1, 0, "Port: %i Handshakes: %i Throttle: %i KiB Loops: %i", + mvprintw(maxY - 1, 0, "Port: %i Handshakes: %i Throttle: %i KiB Http: %i", (int)torrent::get(torrent::LISTEN_PORT), (int)torrent::get(torrent::HANDSHAKES_TOTAL), (int)torrent::get(torrent::THROTTLE_ROOT_CONST_RATE) / 1000, - loops); + httpList.size()); refresh(); } diff --git a/client/http.cc b/client/http.cc new file mode 100644 index 0000000000000000000000000000000000000000..b88a2d91e8a0c2612ff165f010e900e4f0ad0f6d --- /dev/null +++ b/client/http.cc @@ -0,0 +1,54 @@ +#include <sstream> +#include <algo/algo.h> +#include <torrent/torrent.h> +#include <torrent/exceptions.h> +#include <fstream> + +#include "http.h" + +extern std::list<std::string> log_entries; + +struct HttpNode { + int m_id; + std::stringstream m_buf; +}; + +std::list<HttpNode*> httpList; + +void http_get(const std::string& url) { + HttpNode* node = new HttpNode; + + node->m_id = torrent::http_get(url, &node->m_buf, &http_success, node, &http_failed, node); + + httpList.push_back(node); +} + +void http_success(void* arg) { + std::list<HttpNode*>::iterator itr = std::find(httpList.begin(), httpList.end(), (HttpNode*)arg); + + if (itr == httpList.end()) { + log_entries.push_front("Http success, but client doesn't have the node"); + return; + + } else { + log_entries.push_front("Http success"); + } + + try { + torrent::DItr dItr = torrent::create((*itr)->m_buf); + + torrent::start(dItr); + + } catch (torrent::local_error& e) { + log_entries.push_front("Could not start torrent: " + std::string(e.what())); + } + + delete *itr; + + httpList.erase(itr); +} + +void http_failed(void* arg) { + log_entries.push_front("Http failed"); +} + diff --git a/client/http.h b/client/http.h new file mode 100644 index 0000000000000000000000000000000000000000..899213932513904ff3ef7bfc2a4c24fd18c0f070 --- /dev/null +++ b/client/http.h @@ -0,0 +1,14 @@ +#ifndef HTTP_H +#define HTTP_H + +void http_get(const std::string& url); + +void http_success(void* arg); + +void http_failed(void* arg); + +struct HttpNode; + +extern std::list<HttpNode*> httpList; + +#endif diff --git a/client/rtorrent.cc b/client/rtorrent.cc index 19b47aebc67aa0c0549663b6462ca3fa06dc43e9..1f820031ef418d202b6998425711d473fe9c5ddb 100644 --- a/client/rtorrent.cc +++ b/client/rtorrent.cc @@ -10,6 +10,7 @@ #include "display.h" #include "download.h" +#include "http.h" // Uncomment this if your system doesn't have execinfo.h #define USE_EXECINFO @@ -20,6 +21,9 @@ std::list<std::string> log_entries; +bool inputActive = false; +std::string inputBuf; + Display* display = NULL; bool shutdown = false; @@ -98,14 +102,21 @@ int main(int argc, char** argv) { DisplayState displayState = DISPLAY_MAIN; for (fIndex = 1; fIndex < argc; ++fIndex) { - std::fstream f(argv[fIndex], std::ios::in); - - if (!f.is_open()) - continue; - torrent::DList::const_iterator dItr = torrent::create(f); - - torrent::start(dItr); + if (std::strncmp(argv[fIndex], "http://", 7) == 0) { + // Found an http url, download. + http_get(argv[fIndex]); + + } else { + std::fstream f(argv[fIndex], std::ios::in); + + if (!f.is_open()) + continue; + + torrent::DList::const_iterator dItr = torrent::create(f); + + torrent::start(dItr); + } } fIndex = 0; @@ -175,82 +186,111 @@ int main(int argc, char** argv) { lastDraw -= (1 << 30); - int c = getch(); - int64_t constRate = torrent::get(torrent::THROTTLE_ROOT_CONST_RATE); + int c; - switch (c) { - case 'a': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 1000); - break; + while ((c = getch()) != ERR) { - case 'z': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 1000); - break; + if (inputActive) { + if (c == '\n') { + try { + http_get(inputBuf); + } catch (torrent::input_error& e) {} - case 's': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 5000); - break; + inputActive = false; - case 'x': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 5000); - break; + } else if (c == KEY_BACKSPACE) { + if (inputBuf.length()) + inputBuf.resize(inputBuf.length()); - case 'd': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 50000); - break; + } else if (c >= ' ' && c <= '~') { + inputBuf += (char)c; + } - case 'c': - torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 50000); - break; + continue; + } - default: - switch (displayState) { - case DISPLAY_MAIN: - switch (c) { - case KEY_DOWN: - ++curDownload; + int64_t constRate = torrent::get(torrent::THROTTLE_ROOT_CONST_RATE); + + switch (c) { + case 'a': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 1000); + break; + + case 'z': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 1000); + break; + + case 's': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 5000); + break; + + case 'x': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 5000); + break; + + case 'd': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate + 50000); + break; + + case 'c': + torrent::set(torrent::THROTTLE_ROOT_CONST_RATE, constRate - 50000); + break; + + default: + switch (displayState) { + case DISPLAY_MAIN: + switch (c) { + case '\n': + case KEY_ENTER: + inputActive = true; + inputBuf.clear(); + break; + + case KEY_DOWN: + ++curDownload; - break; + break; - case KEY_UP: - --curDownload; + case KEY_UP: + --curDownload; - break; + break; - case KEY_RIGHT: - if (curDownload != torrent::downloads().end()) { - download = Download(curDownload); - displayState = DISPLAY_DOWNLOAD; + case KEY_RIGHT: + if (curDownload != torrent::downloads().end()) { + download = Download(curDownload); + displayState = DISPLAY_DOWNLOAD; + } + + break; + + case 'l': + displayState = DISPLAY_LOG; + break; + + default: + break; } break; - case 'l': - displayState = DISPLAY_LOG; + case DISPLAY_DOWNLOAD: + displayState = download.key(c) ? DISPLAY_DOWNLOAD : DISPLAY_MAIN; break; - default: - break; + case DISPLAY_LOG: + switch (c) { + case '\n': + case ' ': + displayState = DISPLAY_MAIN; + break; + default: + break; + } } break; - - case DISPLAY_DOWNLOAD: - displayState = download.key(c) ? DISPLAY_DOWNLOAD : DISPLAY_MAIN; - break; - - case DISPLAY_LOG: - switch (c) { - case '\n': - case ' ': - displayState = DISPLAY_MAIN; - break; - default: - break; - } } - - break; } } diff --git a/torrent/Makefile.am b/torrent/Makefile.am index f59ff5268ad32c81ac7c48758d1876dc4250679f..0848864ab2f25466ff24e9b27feb0580dbe2bd0d 100644 --- a/torrent/Makefile.am +++ b/torrent/Makefile.am @@ -22,8 +22,10 @@ libtorrent_la_SOURCES = \ files_check.h \ general.cc \ general.h \ - http.cc \ - http.h \ + http_get.cc \ + http_get.h \ + http_list.cc \ + http_list.h \ listen.cc \ listen.h \ peer.cc \ diff --git a/torrent/bitfield.h b/torrent/bitfield.h index f9f37d2fb63a4bf895cd917e7046a0e5276b5a48..8ea96f9e45b45362531c35dfbc57b029b59c6d23 100644 --- a/torrent/bitfield.h +++ b/torrent/bitfield.h @@ -39,6 +39,10 @@ public: m_start[i / 8] &= ~(1 << 7 - i % 8); } + bool get(unsigned int i) const { + return m_start[i / 8] & (1 << 7 - i % 8); + } + bool operator [] (unsigned int i) const { return m_start[i / 8] & (1 << 7 - i % 8); } diff --git a/torrent/delegator.cc b/torrent/delegator.cc index 6855a8a8718b8b0b549eb48cafe0784fdef44266..d8ecd8450a65ec4812c986ed85ef5c1b353bf6b7 100644 --- a/torrent/delegator.cc +++ b/torrent/delegator.cc @@ -52,12 +52,15 @@ bool Delegator::delegate(const std::string& id, const BitField& bf, std::list<Pi // Find a piece that is not queued by anyone. "" and NONE. std::find_if(m_chunks.begin(), m_chunks.end(), - find_if_on(member(&Chunk::m_pieces), - - bool_and(eq(member(&PieceInfo::m_state), value(NONE)), - eq(member(&PieceInfo::m_id), value(""))), + + bool_and(call_member(ref(bf), &BitField::get, member(&Chunk::m_index)), + + find_if_on(member(&Chunk::m_pieces), + + bool_and(eq(member(&PieceInfo::m_state), value(NONE)), + eq(member(&PieceInfo::m_id), value(""))), - assign_ref(target, back_as_ptr()))); + assign_ref(target, back_as_ptr())))); if (target) goto DC_designate_return; @@ -70,29 +73,36 @@ bool Delegator::delegate(const std::string& id, const BitField& bf, std::list<Pi // else find a piece that is queued, but cancelled. "*" and NONE. std::find_if(m_chunks.begin(), m_chunks.end(), - find_if_on(member(&Chunk::m_pieces), - - bool_and(eq(member(&PieceInfo::m_state), value(NONE)), - bool_not(contained_in(ref(pieces), - member(&PieceInfo::m_piece)))), - assign_ref(target, back_as_ptr()))); + bool_and(call_member(ref(bf), &BitField::get, member(&Chunk::m_index)), + + find_if_on(member(&Chunk::m_pieces), + + bool_and(eq(member(&PieceInfo::m_state), value(NONE)), + bool_not(contained_in(ref(pieces), + member(&PieceInfo::m_piece)))), + + assign_ref(target, back_as_ptr())))); if (target) goto DC_designate_return; + // TODO: Write this asap //else if (many chunks left // else find piece that is being downloaded. "*" and DOWNLOADING. // TODO: This will only happen when there are a few pieces left. FIXME std::find_if(m_chunks.begin(), m_chunks.end(), - find_if_on(member(&Chunk::m_pieces), - - bool_and(eq(member(&PieceInfo::m_state), value(DOWNLOADING)), - bool_not(contained_in(ref(pieces), - member(&PieceInfo::m_piece)))), - assign_ref(target, back_as_ptr()))); + bool_and(call_member(ref(bf), &BitField::get, member(&Chunk::m_index)), + + find_if_on(member(&Chunk::m_pieces), + + bool_and(eq(member(&PieceInfo::m_state), value(DOWNLOADING)), + bool_not(contained_in(ref(pieces), + member(&PieceInfo::m_piece)))), + + assign_ref(target, back_as_ptr())))); if (target) goto DC_designate_return; @@ -236,13 +246,13 @@ int Delegator::findChunk(const BitField& bf) { // This byte has some interesting chunks. for (int i = 0; i < 8; ++i) - if (*cur & ((1 << 7) >> i) && + if (*cur & (1 << 7 - i) && std::abs(m_state->bfCounter().field()[(cur - bf.data()) * 8 + i] - target) < selectedDistance) { // Found a closer match selectedIndex = (cur - bf.data()) * 8 + i; selectedDistance = std::abs(m_state->bfCounter().field()[selectedIndex] - target); - if (!target) + if (selectedDistance == 0) break; } diff --git a/torrent/http.cc b/torrent/http_get.cc similarity index 76% rename from torrent/http.cc rename to torrent/http_get.cc index e8a050af4b94fbe39e3562d6c6d3d678b9f04864..3c29be9a27a2b4b016fcf63c8fff25ff3b8c2c3c 100644 --- a/torrent/http.cc +++ b/torrent/http_get.cc @@ -3,7 +3,7 @@ #endif #include "exceptions.h" -#include "http.h" +#include "http_get.h" #include "service.h" #include "settings.h" @@ -14,7 +14,7 @@ namespace torrent { -Http::Http() : +HttpGet::HttpGet() : m_fd(-1), m_buf(NULL), m_code(0), @@ -23,11 +23,11 @@ Http::Http() : m_failedService(NULL) { } -Http::~Http() { +HttpGet::~HttpGet() { close(); } -void Http::set_url(const std::string& url) { +void HttpGet::set_url(const std::string& url) { // TODO: Don't change in the midle of a request. int port, s; @@ -42,34 +42,34 @@ void Http::set_url(const std::string& url) { if ((s = std::sscanf(url.c_str(), "http://%256[^:]:%i/%1024s", hostBuf, &port, pathBuf)) != 3 && (s = std::sscanf(url.c_str(), "http://%256[^/]/%1024s", hostBuf, pathBuf)) != 2) - throw input_error("Http::start() received bad URL"); + throw input_error("HttpGet::start() received bad URL"); if (s == 2) port = 80; if (port <= 0 || port >= 1 << 16) - throw input_error("Http::start() received bad port number"); + throw input_error("HttpGet::start() received bad port number"); m_host = hostBuf; m_path = pathBuf; m_port = port; } -void Http::set_out(std::ostream* out) { +void HttpGet::set_out(std::ostream* out) { m_out = out; } -void Http::set_success(Service* service, int type) { +void HttpGet::set_success(Service* service, int type) { m_successService = service; m_successType = type; } -void Http::set_failed(Service* service, int type) { +void HttpGet::set_failed(Service* service, int type) { m_failedService = service; m_failedType = type; } -void Http::start() { +void HttpGet::start() { close(); sockaddr_in sa; @@ -86,18 +86,19 @@ void Http::start() { insertExcept(); } -void Http::close() { +void HttpGet::close() { if (m_fd < 0) return; ::close(m_fd); + delete m_buf; m_fd = -1; m_buf = NULL; } -void Http::read() { +void HttpGet::read() { try { if (m_bufEnd == -1) { @@ -108,7 +109,7 @@ void Http::read() { } else if (m_bufEnd > 0) { // Content with a fixed length. - readBuf(m_buf, m_bufEnd, m_bufPos = 0); + readBuf(m_buf, std::min(m_bufEnd, m_bufSize), m_bufPos = 0); if (m_out) m_out->write(m_buf, m_bufPos); @@ -139,7 +140,7 @@ void Http::read() { } } -void Http::write() { +void HttpGet::write() { try { if (m_bufEnd == 0) { @@ -160,7 +161,7 @@ void Http::write() { Settings::httpName.c_str()); if (m_bufEnd < 0 || m_bufEnd >= m_bufSize) - internal_error("Http request snprintf failed, overflow or other error"); + internal_error("HttpGet request snprintf failed, overflow or other error"); } if (!writeBuf(m_buf + m_bufPos, m_bufEnd, m_bufPos)) @@ -177,7 +178,7 @@ void Http::write() { } } -void Http::except() { +void HttpGet::except() { close(); if (m_failedService) @@ -185,7 +186,7 @@ void Http::except() { } // ParseHeader throws closeConnection if done -void Http::parse_header() { +void HttpGet::parse_header() { int a, size = -1; std::string buf; @@ -210,28 +211,46 @@ void Http::parse_header() { size = a; } + if (!buf.length()) + break; + buf.clear(); } else if (*pos != '\r') { buf += *pos; } - } while (*(pos++) != '\n' || buf.length()); + ++pos; + + } while (true); + + ++pos; if (m_code == 200) { - if (m_out) - m_out->write(pos, end - pos); - + m_bufEnd = 0; + + if (size < 0) { + if (m_out) + m_out->write(pos, end - pos); - if (size == 0) - throw close_connection(); - else if (size > 0) - m_bufEnd = size; + } else { + if (m_out) + m_out->write(pos, std::min(end - pos, size)); + + if (size == std::min(end - pos, size)) + throw close_connection(); + else + m_bufEnd = size - (end - pos); + } } else { throw network_error("Returned bad http code"); } } +int HttpGet::fd() { + return m_fd; +} + } diff --git a/torrent/http.h b/torrent/http_get.h similarity index 81% rename from torrent/http.h rename to torrent/http_get.h index 6449b60d0e522441f8f5b1b3c6f19fb9fed0981b..b64076d50572eb87304b7c508aa950027b32c0c4 100644 --- a/torrent/http.h +++ b/torrent/http_get.h @@ -1,5 +1,5 @@ -#ifndef LIBTORRENT_HTTP_H -#define LIBTORRENT_HTTP_H +#ifndef LIBTORRENT_HTTP_GET_H +#define LIBTORRENT_HTTP_GET_H #include <string> #include <iosfwd> @@ -10,10 +10,10 @@ namespace torrent { class Service; -class Http : public SocketBase { +class HttpGet : public SocketBase { public: - Http(); - ~Http(); + HttpGet(); + ~HttpGet(); void set_url(const std::string& url); void set_out(std::ostream* out); @@ -30,7 +30,11 @@ class Http : public SocketBase { virtual void write(); virtual void except(); + virtual int fd(); + private: + HttpGet(const HttpGet&); + void operator = (const HttpGet&); void parse_header(); diff --git a/torrent/http_list.cc b/torrent/http_list.cc new file mode 100644 index 0000000000000000000000000000000000000000..4d29dd01d15737f1b7a184280dccee5c61ebb559 --- /dev/null +++ b/torrent/http_list.cc @@ -0,0 +1,87 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "exceptions.h" +#include "http_list.h" +#include "http_get.h" + +#include <algo/algo.h> + +namespace torrent { + +using namespace algo; + +HttpList::HttpList() : + m_nextId(0) { +} + +HttpList::~HttpList() { + cleanup(); +} + +int HttpList::get(const std::string& url, + std::ostream* output, + Func success, + void* successArg, + Func failed, + void* failedArg) { + + HttpGet* http = new HttpGet(); + + http->set_url(url); + http->set_out(output); + http->set_success(this, m_nextId); + http->set_failed(this, m_nextId + 1); + + http->start(); + + m_list.push_back(Node(m_nextId, success, successArg, failed, failedArg, http)); + + m_nextId += 2; + + return m_nextId - 2; +} + +void HttpList::cancel(int id) { + List::iterator itr = std::find_if(m_list.begin(), m_list.end(), + eq(member(&Node::m_id), value(id))); + + if (itr != m_list.end()) { + delete itr->m_http; + + m_list.erase(itr); + } +} + +void HttpList::cleanup() { + while (!m_list.empty()) { + delete m_list.front().m_http; + + m_list.pop_front(); + } +} + +void HttpList::service(int type) { + int id = (type >> 1) << 1; + + List::iterator itr = std::find_if(m_list.begin(), m_list.end(), + eq(member(&Node::m_id), value(id))); + + if (itr == m_list.end()) + throw internal_error("HttpList::service(...) called with unknown type"); + + if (type % 2) { + itr->m_func1(itr->m_arg1); + + } else { + itr->m_func0(itr->m_arg0); + } + + delete itr->m_http; + + m_list.erase(itr); +} + +} + diff --git a/torrent/http_list.h b/torrent/http_list.h new file mode 100644 index 0000000000000000000000000000000000000000..61192ce1bac20cf916233ebc6e62f97e7dbc1f1a --- /dev/null +++ b/torrent/http_list.h @@ -0,0 +1,60 @@ +#ifndef LIBTORRENT_HTTP_LIST_H +#define LIBTORRENT_HTTP_LIST_H + +#include <list> +#include <iosfwd> +#include "service.h" + +namespace torrent { + +class HttpGet; + +class HttpList : public Service { + public: + typedef void (*Func)(void*); + + struct Node { + Node(int id, + Func func0, void* arg0, + Func func1, void* arg1, HttpGet* http) : + m_id(id), m_func0(func0), m_arg0(arg0), + m_func1(func1), m_arg1(arg1), m_http(http) {} + + int m_id; + + Func m_func0; + void* m_arg0; + + Func m_func1; + void* m_arg1; + + HttpGet* m_http; + }; + + typedef std::list<Node> List; + + HttpList(); + ~HttpList(); + + int get(const std::string& url, + std::ostream* output, + Func success, + void* successArg, + Func failed, + void* failedArg); + + void cancel(int id); + void cleanup(); + + void service(int type); + + private: + + int m_nextId; + + List m_list; +}; + +} + +#endif diff --git a/torrent/torrent.cc b/torrent/torrent.cc index 29e0836697f2ccedecbf6abed5500f6c0e497501..ae673872ed41dc43137aecd5853760672d91f6e6 100644 --- a/torrent/torrent.cc +++ b/torrent/torrent.cc @@ -10,6 +10,7 @@ #include "exceptions.h" #include "download.h" #include "general.h" +#include "http_list.h" #include "listen.h" #include "peer_handshake.h" #include "peer_connection.h" @@ -23,6 +24,8 @@ using namespace algo; namespace torrent { +HttpList httpList; + int64_t Timer::m_cache; std::list<std::string> caughtExceptions; @@ -65,6 +68,7 @@ void initialize(int beginPort, int endPort) { void shutdown() { Listen::close(); + httpList.cleanup(); std::for_each(Download::downloads().begin(), Download::downloads().end(), call_member(&Download::stop)); @@ -75,6 +79,7 @@ void shutdown() { void cleanup() { // Close again if shutdown wasn't called. Listen::close(); + httpList.cleanup(); ThrottleControl::global().removeService(); @@ -526,4 +531,17 @@ void set(DList::const_iterator d, DValue t, int64_t v) { void set(DList::const_iterator d, DString t, const std::string& s) { } +int http_get(const std::string& url, + std::ostream* output, + HttpFunc success, void* successArg, + HttpFunc failed, void* failedArg) { + return httpList.get(url, output, + success, successArg, + failed, failedArg); +} + +void http_cancel(int id) { + httpList.cancel(id); +} + } diff --git a/torrent/torrent.h b/torrent/torrent.h index 4cf3db3a7a55dc0a15089fc5a5ee7268addc68df..9fcfe1b7f38a4e28253a697ae63a48e0c78eb546 100644 --- a/torrent/torrent.h +++ b/torrent/torrent.h @@ -5,6 +5,7 @@ #include <string> #include <inttypes.h> #include <sys/types.h> +#include <iosfwd> namespace torrent { @@ -163,6 +164,20 @@ void set(DList::const_iterator d, DValue t, int64_t v); void set(GString t, const std::string& s); void set(DList::const_iterator d, DString t, const std::string& s); +// Temporary interface for doing http downloads. Since we need to +// implement it for the tracker we might as well use it for other stuff. + +// Could we perhaps use signature class or something for success/fail? + +typedef void (*HttpFunc)(void*); + +int http_get(const std::string& url, + std::ostream* output, + HttpFunc success, void* successArg, + HttpFunc failed, void* failedArg); + +void http_cancel(int id); + } #endif // LIBTORRENT_TORRENT_H diff --git a/torrent/tracker_query.cc b/torrent/tracker_query.cc index 9a4e2a30c65678d80e46c324174f4c14e27df3a5..e705971b4fa13603fab3e4980687d7b53ec84a39 100644 --- a/torrent/tracker_query.cc +++ b/torrent/tracker_query.cc @@ -172,10 +172,17 @@ void TrackerQuery::sendState() { } void TrackerQuery::escapeString(const std::string& src, std::ostream& stream) { + // TODO: Correct would be to save the state. stream << std::hex << std::uppercase; for (std::string::const_iterator itr = src.begin(); itr != src.end(); ++itr) - stream << '%' << ((unsigned char)*itr >> 4) << ((unsigned char)*itr & 0xf); + if ((*itr >= 'A' && *itr <= 'Z') || + (*itr >= 'a' && *itr <= 'z') || + (*itr >= '0' && *itr <= '9') || + *itr == '-') + stream << *itr; + else + stream << '%' << ((unsigned char)*itr >> 4) << ((unsigned char)*itr & 0xf); stream << std::dec << std::nouppercase; }