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;
 }