/* * Copyright © 2006 Alon Zakai ('Kripken') * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Thank You: Some code portions were derived from BSD-licensed work by * Arvid Norberg, and GPL-licensed work by Christophe Dumez */ //------------------ // TODO: // // The DHT capability requires UDP. We need to check that this port is in fact // open, just like the normal TCP port for bittorrent. // // Wait for answers about what to do with the router.*torrent.com's... // //------------------ //----------------- // INCLUDES //----------------- #include #include #include #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/session.hpp" #include "libtorrent/identify_client.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/ip_filter.hpp" using namespace libtorrent; //----------------- // CONSTANTS //----------------- #ifdef AMD64 #define python_long int #else #define python_long long #endif #define EVENT_NULL 0 #define EVENT_FINISHED 1 #define EVENT_PEER_ERROR 2 #define EVENT_INVALID_REQUEST 3 #define EVENT_FILE_ERROR 4 #define EVENT_HASH_FAILED_ERROR 5 #define EVENT_PEER_BAN_ERROR 6 #define EVENT_FASTRESUME_REJECTED_ERROR 8 #define EVENT_TRACKER 9 #define EVENT_OTHER 10 #define STATE_QUEUED 0 #define STATE_CHECKING 1 #define STATE_CONNECTING 2 #define STATE_DOWNLOADING_META 3 #define STATE_DOWNLOADING 4 #define STATE_FINISHED 5 #define STATE_SEEDING 6 #define STATE_ALLOCATING 7 #define DHT_ROUTER_PORT 6881 //----------------- // TYPES //----------------- typedef long unique_ID_t; typedef std::vector filter_out_t; typedef std::string torrent_name_t; struct torrent_t { torrent_handle handle; unique_ID_t unique_ID; }; typedef std::vector torrents_t; typedef torrents_t::iterator torrents_t_iterator; //--------------------------- // MODULE-GLOBAL VARIABLES //--------------------------- long M_unique_counter = 0; session_settings *M_settings = NULL; session *M_ses = NULL; PyObject *M_constants = NULL; ip_filter *M_the_filter = NULL; torrents_t *M_torrents = NULL; //------------------------ // Exception types & macro //------------------------ static PyObject *PyTorrentCoreError; static PyObject *InvalidEncodingError; static PyObject *FilesystemError; static PyObject *DuplicateTorrentError; static PyObject *InvalidTorrentError; #define PYTORRENTCORE_RAISE_PTR(e,s) { printf("Raising error: %s\r\n", s); PyErr_SetString(e, s); return NULL; } #define PYTORRENTCORE_RAISE_INT(e,s) { printf("Raising error: %s\r\n", s); PyErr_SetString(e, s); return -1; } //--------------------- // Internal functions //--------------------- bool empty_name_check(const std::string & name) { return 1; } long handle_exists(torrent_handle &handle) { for (unsigned long i = 0; i < M_torrents->size(); i++) if ((*M_torrents)[i].handle == handle) return 1; return 0; } long get_torrent_index(torrent_handle &handle) { for (unsigned long i = 0; i < M_torrents->size(); i++) if ((*M_torrents)[i].handle == handle) { // printf("Found: %li\r\n", i); return i; } PYTORRENTCORE_RAISE_INT(PyTorrentCoreError, "Handle not found."); } long get_index_from_unique_ID(long unique_ID) { assert(M_handles->size() == M_unique_IDs->size()); for (unsigned long i = 0; i < M_torrents->size(); i++) if ((*M_torrents)[i].unique_ID == unique_ID) return i; PYTORRENTCORE_RAISE_INT(PyTorrentCoreError, "No such unique_ID."); } long internal_add_torrent(std::string const& torrent_name, float preferred_ratio, bool compact_mode, boost::filesystem::path const& save_path) { std::ifstream in(torrent_name.c_str(), std::ios_base::binary); in.unsetf(std::ios_base::skipws); entry e; e = bdecode(std::istream_iterator(in), std::istream_iterator()); torrent_info t(e); entry resume_data; try { std::stringstream s; s << torrent_name << ".fastresume"; boost::filesystem::ifstream resumeFile(s.str(), std::ios_base::binary); resumeFile.unsetf(std::ios_base::skipws); resume_data = bdecode(std::istream_iterator(resumeFile), std::istream_iterator()); } catch (invalid_encoding&) {} catch (boost::filesystem::filesystem_error&) {} // Create new torrent object torrent_t new_torrent; torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, compact_mode, 16 * 1024); // h.set_max_connections(60); // at some point we should use this h.set_max_uploads(-1); h.set_ratio(preferred_ratio); new_torrent.handle = h; new_torrent.unique_ID = M_unique_counter; M_unique_counter++; M_torrents->push_back(new_torrent); return (new_torrent.unique_ID); } void internal_remove_torrent(long index) { assert(index < M_torrents->size()); torrent_handle& h = M_torrents->at(index).handle; M_ses->remove_torrent(h); torrents_t_iterator it = M_torrents->begin() + index; M_torrents->erase(it); } long get_peer_index(tcp::endpoint addr, std::vector const& peers) { long index = -1; for (unsigned long i = 0; i < peers.size(); i++) if (peers[i].ip == addr) index = i; return index; } // The following function contains code by Christophe Dumez and Arvid Norberg void internal_add_files(torrent_info& t, boost::filesystem::path const& p, boost::filesystem::path const& l) { boost::filesystem::path f(p / l); // change default checker, perhaps? if (is_directory(f)) { for (boost::filesystem::directory_iterator i(f), end; i != end; ++i) internal_add_files(t, p, l / i->leaf()); } else t.add_file(l, file_size(f)); } long count_DHT_peers(entry &state) { long num_peers = 0; entry *nodes = state.find_key("nodes"); if (nodes) { entry::list_type &peers = nodes->list(); entry::list_type::const_iterator i; i = peers.begin(); while (i != peers.end()) { num_peers++; i++; } } return num_peers; } //===================== // External functions //===================== static PyObject *torrent_init(PyObject *self, PyObject *args) { printf("pytorrent_core, using libtorrent %s. Compiled with NDEBUG value: %d\r\n", LIBTORRENT_VERSION, NDEBUG); // Tell Boost that we are on *NIX, so bloody '.'s are ok inside a directory name! boost::filesystem::path::default_name_check(empty_name_check); char *client_ID, *user_agent; python_long v1,v2,v3,v4; if (!PyArg_ParseTuple(args, "siiiis", &client_ID, &v1, &v2, &v3, &v4, &user_agent)) return NULL; M_settings = new session_settings; M_ses = new session(fingerprint(client_ID, v1, v2, v3, v4)); M_torrents = new torrents_t; M_torrents->reserve(10); // pretty cheap, just 10 // Init values M_settings->user_agent = std::string(user_agent); M_ses->set_max_half_open_connections(-1); M_ses->set_download_rate_limit(-1); M_ses->set_upload_rate_limit(-1); M_ses->set_settings(*M_settings); M_ses->set_severity_level(alert::debug); M_constants = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}", "EVENT_NULL", EVENT_NULL, "EVENT_FINISHED", EVENT_FINISHED, "EVENT_PEER_ERROR", EVENT_PEER_ERROR, "EVENT_INVALID_REQUEST", EVENT_INVALID_REQUEST, "EVENT_FILE_ERROR", EVENT_FILE_ERROR, "EVENT_HASH_FAILED_ERROR", EVENT_HASH_FAILED_ERROR, "EVENT_PEER_BAN_ERROR", EVENT_PEER_BAN_ERROR, "EVENT_FASTRESUME_REJECTED_ERROR", EVENT_FASTRESUME_REJECTED_ERROR, "EVENT_TRACKER", EVENT_TRACKER, "EVENT_OTHER", EVENT_OTHER, "STATE_QUEUED", STATE_QUEUED, "STATE_CHECKING", STATE_CHECKING, "STATE_CONNECTING", STATE_CONNECTING, "STATE_DOWNLOADING_META", STATE_DOWNLOADING_META, "STATE_DOWNLOADING", STATE_DOWNLOADING, "STATE_FINISHED", STATE_FINISHED, "STATE_SEEDING", STATE_SEEDING, "STATE_ALLOCATING", STATE_ALLOCATING); Py_INCREF(Py_None); return Py_None; }; static PyObject *torrent_quit(PyObject *self, PyObject *args) { delete M_ses; // SLOWPOKE because of waiting for the trackers before shutting down delete M_settings; delete M_torrents; Py_DECREF(M_constants); Py_INCREF(Py_None); return Py_None; }; static PyObject *torrent_save_fastresume(PyObject *self, PyObject *args) { python_long unique_ID; const char *torrent_name; if (!PyArg_ParseTuple(args, "is", &unique_ID, &torrent_name)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; torrent_handle& h = M_torrents->at(index).handle; // For valid torrents, save fastresume data if (h.is_valid() && h.has_metadata()) { h.pause(); entry data = h.write_resume_data(); std::stringstream s; s << torrent_name << ".fastresume"; boost::filesystem::ofstream out(s.str(), std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), data); } else PYTORRENTCORE_RAISE_PTR(PyTorrentCoreError, "Invalid handle or no metadata for fastresume."); } static PyObject *torrent_set_max_half_open(PyObject *self, PyObject *args) { python_long arg; if (!PyArg_ParseTuple(args, "i", &arg)) return NULL; M_ses->set_max_half_open_connections(arg); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_set_download_rate_limit(PyObject *self, PyObject *args) { python_long arg; if (!PyArg_ParseTuple(args, "i", &arg)) return NULL; // printf("Capping download to %d bytes per second\r\n", (int)arg); M_ses->set_download_rate_limit(arg); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_set_upload_rate_limit(PyObject *self, PyObject *args) { python_long arg; if (!PyArg_ParseTuple(args, "i", &arg)) return NULL; // printf("Capping upload to %d bytes per second\r\n", (int)arg); M_ses->set_upload_rate_limit(arg); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_set_listen_on(PyObject *self, PyObject *args) { python_long port_start, port_end; if (!PyArg_ParseTuple(args, "ii", &port_start, &port_end)) return NULL; M_ses->listen_on(std::make_pair(port_start, port_end), ""); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_is_listening(PyObject *self, PyObject *args) { long ret = (M_ses->is_listening() != 0); return Py_BuildValue("i", ret); } static PyObject *torrent_listening_port(PyObject *self, PyObject *args) { return Py_BuildValue("i", (python_long)M_ses->listen_port()); } static PyObject *torrent_set_max_uploads(PyObject *self, PyObject *args) { python_long max_up; if (!PyArg_ParseTuple(args, "i", &max_up)) return NULL; M_ses->set_max_uploads(max_up); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_set_max_connections(PyObject *self, PyObject *args) { python_long max_conn; if (!PyArg_ParseTuple(args, "i", &max_conn)) return NULL; // printf("Setting max connections: %d\r\n", max_conn); M_ses->set_max_connections(max_conn); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_add_torrent(PyObject *self, PyObject *args) { const char *name, *save_dir; python_long compact; if (!PyArg_ParseTuple(args, "ssi", &name, &save_dir, &compact)) return NULL; boost::filesystem::path save_dir_2 (save_dir, empty_name_check); try { long ret = internal_add_torrent(name, 0, compact, save_dir_2); if (PyErr_Occurred()) return NULL; else return Py_BuildValue("i", ret); } catch (invalid_encoding&) { PYTORRENTCORE_RAISE_PTR(InvalidEncodingError, ""); } catch (invalid_torrent_file&) { PYTORRENTCORE_RAISE_PTR(InvalidTorrentError, ""); } catch (boost::filesystem::filesystem_error&) { PYTORRENTCORE_RAISE_PTR(FilesystemError, ""); } catch (duplicate_torrent&) { PYTORRENTCORE_RAISE_PTR(DuplicateTorrentError, ""); } } static PyObject *torrent_remove_torrent(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; internal_remove_torrent(index); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_get_num_torrents(PyObject *self, PyObject *args) { return Py_BuildValue("i", M_torrents->size()); } static PyObject *torrent_reannounce(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; M_torrents->at(index).handle.force_reannounce(); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_is_seeding(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; return Py_BuildValue("i", M_torrents->at(index).handle.is_seed()); } static PyObject *torrent_is_paused(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; return Py_BuildValue("i", M_torrents->at(index).handle.is_paused()); } static PyObject *torrent_pause(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; M_torrents->at(index).handle.pause(); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_resume(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; M_torrents->at(index).handle.resume(); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_get_torrent_info(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; torrent_t &t = M_torrents->at(index); return Py_BuildValue("{s:s,s:l}", "name", t.handle.get_torrent_info().name().c_str(), "num_files", t.handle.get_torrent_info().num_files() ); } static PyObject *torrent_get_state(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; torrent_t &t = M_torrents->at(index); torrent_status s = t.handle.status(); const torrent_info &i = t.handle.get_torrent_info(); std::vector peers; t.handle.get_peer_info(peers); long total_seeds = 0; long total_peers = 0; for (unsigned long i = 0; i < peers.size(); i++) if (peers[i].seed) total_seeds++; else total_peers++; return Py_BuildValue("{s:l,s:l,s:l,s:f,s:f,s:d,s:f,s:l,s:l,s:s,s:s,s:f,s:d,s:l,s:l,s:l,s:d,s:l,s:l,s:l,s:l,s:l,s:l,s:d,s:d,s:l,s:l}", "state", s.state, "num_peers", s.num_peers, "num_seeds", s.num_seeds, "distributed_copies", s.distributed_copies, "download_rate", s.download_rate, "total_download", double(s.total_download), // WAS: payload in the middle there "upload_rate", s.upload_rate, "total_upload", long(s.total_upload), // WAS: payload //"ratio", float(-1),//float(s.total_payload_download)/float(s.total_payload_upload), "tracker_ok", !s.current_tracker.empty(), "next_announce", boost::posix_time::to_simple_string(s.next_announce).c_str(), "tracker", s.current_tracker.c_str(), "progress", float(s.progress), "total_done", double(s.total_done), "pieces", long(s.pieces), "pieces_done", long(s.num_pieces), "block_size", long(s.block_size), "total_size", double(i.total_size()), "piece_length", long(i.piece_length()), "num_pieces", long(i.num_pieces()), "total_seeds", total_seeds, "total_peers", total_peers, "is_paused", long(t.handle.is_paused()), "is_seed", long(t.handle.is_seed()), "total_wanted", double(s.total_wanted), "total_wanted_done", double(s.total_wanted_done), "num_complete", long(s.num_complete), "num_incomplete", long(s.num_incomplete)); }; static PyObject *torrent_pop_event(PyObject *self, PyObject *args) { std::auto_ptr a; a = M_ses->pop_alert(); alert *popped_alert = a.get(); if (!popped_alert) { Py_INCREF(Py_None); return Py_None; } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i}", "event_type", EVENT_FINISHED, "unique_ID", M_torrents->at(index).unique_ID); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { peer_id peer_ID = (dynamic_cast(popped_alert))->pid; std::string peer_IP = (dynamic_cast(popped_alert))->ip.address().to_string(); return Py_BuildValue("{s:i,s:s,s:s,s:s}", "event_type", EVENT_PEER_ERROR, "client_ID", identify_client(peer_ID).c_str(), "ip", peer_IP.c_str(), "message", a->msg().c_str() ); } else if (dynamic_cast(popped_alert)) { peer_id peer_ID = (dynamic_cast(popped_alert))->pid; return Py_BuildValue("{s:i,s:s,s:s}", "event_type", EVENT_INVALID_REQUEST, "client_ID", identify_client(peer_ID).c_str(), "message", a->msg().c_str() ); } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s}", "event_type", EVENT_FILE_ERROR, "unique_ID", M_torrents->at(index).unique_ID, "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:i,s:s}", "event_type", EVENT_HASH_FAILED_ERROR, "unique_ID", M_torrents->at(index).unique_ID, "piece_index", long((dynamic_cast(popped_alert))->piece_index), "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; std::string peer_IP = (dynamic_cast(popped_alert))->ip.address().to_string(); if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s,s:s}", "event_type", EVENT_PEER_BAN_ERROR, "unique_ID", M_torrents->at(index).unique_ID, "ip", peer_IP.c_str(), "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s}", "event_type", EVENT_FASTRESUME_REJECTED_ERROR, "unique_ID", M_torrents->at(index).unique_ID, "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s,s:s}", "event_type", EVENT_TRACKER, "unique_ID", M_torrents->at(index).unique_ID, "tracker_status", "Announce sent", "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s,s:s}", "event_type", EVENT_TRACKER, "unique_ID", M_torrents->at(index).unique_ID, "trackerStatus", "Bad response (status code=?)", "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s,s:s}", "event_type", EVENT_TRACKER, "unique_ID", M_torrents->at(index).unique_ID, "tracker_status", "Announce succeeded", "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } else if (dynamic_cast(popped_alert)) { torrent_handle handle = (dynamic_cast(popped_alert))->handle; long index = get_torrent_index(handle); if (PyErr_Occurred()) return NULL; if (handle_exists(handle)) return Py_BuildValue("{s:i,s:i,s:s,s:s}", "event_type", EVENT_TRACKER, "unique_ID", M_torrents->at(index).unique_ID, "tracker_status", "Warning in response", "message", a->msg().c_str() ); else { Py_INCREF(Py_None); return Py_None; } } return Py_BuildValue("{s:i,s:s}", "event_type", EVENT_OTHER, "message", a->msg().c_str() ); } static PyObject *torrent_get_session_info(PyObject *self, PyObject *args) { session_status s = M_ses->status(); return Py_BuildValue("{s:l,s:f,s:f,s:f,s:f,s:l}", "has_incoming_connections", long(s.has_incoming_connections), "upload_rate", float(s.upload_rate), "download_rate", float(s.download_rate), "payload_upload_rate", float(s.payload_upload_rate), "payload_download_rate", float(s.payload_download_rate), "num_peers", long(s.num_peers)); } static PyObject *torrent_get_peer_info(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; std::vector peers; M_torrents->at(index).handle.get_peer_info(peers); PyObject *peer_info; PyObject *ret = PyTuple_New(peers.size()); for (unsigned long i = 0; i < peers.size(); i++) { std::vector &pieces = peers[i].pieces; unsigned long pieces_had = 0; for (unsigned long piece = 0; piece < pieces.size(); piece++) if (pieces[piece]) pieces_had++; peer_info = Py_BuildValue( "{s:f,s:d,s:f,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:s,s:i,s:s,s:f}", "download_speed", float(peers[i].down_speed), "total_download", double(peers[i].total_download), "upload_speed", float(peers[i].up_speed), "total_upload", double(peers[i].total_upload), "download_queue_length", long(peers[i].download_queue_length), "upload_queue_length", long(peers[i].upload_queue_length), "is_interesting", long((peers[i].flags & peer_info::interesting) != 0), "is_choked", long((peers[i].flags & peer_info::choked) != 0), "is_remote_interested", long((peers[i].flags & peer_info::remote_interested) != 0), "is_remote_choked", long((peers[i].flags & peer_info::remote_choked) != 0), "supports_extensions", long((peers[i].flags & peer_info::supports_extensions)!= 0), "is_local_connection", long((peers[i].flags & peer_info::local_connection) != 0), "is_awaiting_handshake", long((peers[i].flags & peer_info::handshake) != 0), "is_connecting", long((peers[i].flags & peer_info::connecting) != 0), "is_queued", long((peers[i].flags & peer_info::queued) != 0), "client", peers[i].client.c_str(), "is_seed", long(peers[i].seed), "ip", peers[i].ip.address().to_string().c_str(), "peer_has", float(float(pieces_had)*100.0/pieces.size()) ); PyTuple_SetItem(ret, i, peer_info); }; return ret; }; static PyObject *torrent_get_file_info(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; std::vector temp_files; PyObject *file_info; std::vector progresses; torrent_t &t = M_torrents->at(index); t.handle.file_progress(progresses); torrent_info::file_iterator start = t.handle.get_torrent_info().begin_files(); torrent_info::file_iterator end = t.handle.get_torrent_info().end_files(); long fileIndex = 0; for(torrent_info::file_iterator i = start; i != end; ++i) { file_entry const &currFile = (*i); file_info = Py_BuildValue( "{s:s,s:d,s:d,s:f}", "path", currFile.path.string().c_str(), "offset", double(currFile.offset), "size", double(currFile.size), "progress", progresses[i - start]*100.0 ); fileIndex++; temp_files.push_back(file_info); }; PyObject *ret = PyTuple_New(temp_files.size()); for (unsigned long i = 0; i < temp_files.size(); i++) PyTuple_SetItem(ret, i, temp_files[i]); return ret; }; static PyObject *torrent_set_filter_out(PyObject *self, PyObject *args) { python_long unique_ID; PyObject *filter_out_object; if (!PyArg_ParseTuple(args, "iO", &unique_ID, &filter_out_object)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; torrent_t &t = M_torrents->at(index); long num_files = t.handle.get_torrent_info().num_files(); assert(PyList_Size(filter_out_object) == num_files); filter_out_t filter_out(num_files); for (long i = 0; i < num_files; i++) { filter_out.at(i) = PyInt_AsLong(PyList_GetItem(filter_out_object, i)); }; t.handle.filter_files(filter_out); Py_INCREF(Py_None); return Py_None; } /*static PyObject *torrent_get_unique_IDs(PyObject *self, PyObject *args) { PyObject *ret = PyTuple_New(M_torrents.size()); PyObject *temp; for (unsigned long i = 0; i < M_torrents.size(); i++) { temp = Py_BuildValue("i", M_torrents->at(i).unique_ID) PyTuple_SetItem(ret, i, temp); }; return ret; };*/ static PyObject *torrent_constants(PyObject *self, PyObject *args) { Py_INCREF(M_constants); return M_constants; } static PyObject *torrent_start_DHT(PyObject *self, PyObject *args) { const char *DHT_path; if (!PyArg_ParseTuple(args, "s", &DHT_path)) return NULL; printf("Loading DHT state from %s\r\n", DHT_path); boost::filesystem::path tempPath(DHT_path, empty_name_check); boost::filesystem::ifstream DHT_state_file(tempPath, std::ios_base::binary); DHT_state_file.unsetf(std::ios_base::skipws); entry DHT_state; try{ DHT_state = bdecode(std::istream_iterator(DHT_state_file), std::istream_iterator()); M_ses->start_dht(DHT_state); printf("DHT state recovered.\r\n"); // Print out the state data from the FILE (not the session!) printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state)); } catch (std::exception&) { printf("No DHT file to resume\r\n"); M_ses->start_dht(); } // M_ses->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), // DHT_ROUTER_PORT)); // M_ses->add_dht_router(std::make_pair(std::string("router.utorrent.com"), // DHT_ROUTER_PORT)); //// M_ses->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), //// DHT_ROUTER_PORT)); Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_stop_DHT(PyObject *self, PyObject *args) { const char *DHT_path; if (!PyArg_ParseTuple(args, "s", &DHT_path)) return NULL; printf("Saving DHT state to %s\r\n", DHT_path); boost::filesystem::path tempPath = boost::filesystem::path(DHT_path, empty_name_check); try { entry DHT_state = M_ses->dht_state(); printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state)); boost::filesystem::ofstream out(tempPath, std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), DHT_state); } catch (std::exception& e) { printf("An error occured in saving DHT\r\n"); std::cerr << e.what() << "\n"; } Py_INCREF(Py_None); return Py_None; } static PyObject *torrent_get_DHT_info(PyObject *self, PyObject *args) { entry DHT_state = M_ses->dht_state(); return Py_BuildValue("l", python_long(count_DHT_peers(DHT_state))); /* // DHT_state.print(cout); entry *nodes = DHT_state.find_key("nodes"); if (!nodes) return Py_BuildValue("l", -1); // No nodes - we are just starting up... entry::list_type &peers = nodes->list(); entry::list_type::const_iterator i; python_long num_peers = 0; i = peers.begin(); while (i != peers.end()) { num_peers++; i++; } return Py_BuildValue("l", num_peers); */ } // Create Torrents: call with something like: // create_torrent("mytorrent.torrent", "directory or file to make a torrent out of", // "tracker1\ntracker2\ntracker3", "no comment", 256, "Deluge"); // That makes a torrent with pieces of 256K, with "Deluge" as the creator string. // // The following function contains code by Christophe Dumez and Arvid Norberg static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) { char *destination, *comment, *creator_str, *input, *trackers; python_long piece_size; if (!PyArg_ParseTuple(args, "ssssis", &destination, &input, &trackers, &comment, &piece_size, &creator_str)) return NULL; piece_size = piece_size * 1024; try { torrent_info t; boost::filesystem::path full_path = complete(boost::filesystem::path(input)); boost::filesystem::ofstream out(complete(boost::filesystem::path(destination)), std::ios_base::binary); internal_add_files(t, full_path.branch_path(), full_path.leaf()); t.set_piece_size(piece_size); storage st(t, full_path.branch_path()); std::string stdTrackers(trackers); unsigned long index = 0, next = stdTrackers.find("\n"); while (1 == 1) { t.add_tracker(stdTrackers.substr(index, next-index)); index = next + 1; if (next >= stdTrackers.length()) break; next = stdTrackers.find("\n", index); if (next == std::string::npos) break; } int num = t.num_pieces(); std::vector buf(piece_size); for (int i = 0; i < num; ++i) { st.read(&buf[0], i, 0, t.piece_size(i)); hasher h(&buf[0], t.piece_size(i)); t.set_hash(i, h.final()); } t.set_creator(creator_str); t.set_comment(comment); entry e = t.create_torrent(); bencode(std::ostream_iterator(out), e); return Py_BuildValue("l", 1); } catch (std::exception& e) { // std::cerr << e.what() << "\n"; // return Py_BuildValue("l", 0); PYTORRENTCORE_RAISE_PTR(PyTorrentCoreError, e.what()); } } static PyObject *torrent_apply_IP_filter(PyObject *self, PyObject *args) { PyObject *ranges; if (!PyArg_ParseTuple(args, "O", &ranges)) return NULL; long num_ranges = PyList_Size(ranges); // printf("Number of ranges: %ld\r\n", num_ranges); // Py_INCREF(Py_None); return Py_None; // Remove existing filter, if there is one if (M_the_filter != NULL) delete M_the_filter; M_the_filter = new ip_filter(); address_v4 from, to; PyObject *curr; // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); for (long i = 0; i < num_ranges; i++) { curr = PyList_GetItem(ranges, i); // PyObject_Print(curr, stdout, 0); from = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 0))); to = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 1))); // printf("Filtering: %s - %s\r\n", from.to_string().c_str(), to.to_string().c_str()); M_the_filter->add_rule(from, to, ip_filter::blocked); }; // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); M_ses->set_ip_filter(*M_the_filter); // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); Py_INCREF(Py_None); return Py_None; } //==================== // Python Module data //==================== static PyMethodDef pytorrent_core_methods[] = { {"init", torrent_init, METH_VARARGS, "."}, {"quit", torrent_quit, METH_VARARGS, "."}, {"save_fastresume", torrent_save_fastresume, METH_VARARGS, "."}, {"set_max_half_open", torrent_set_max_half_open, METH_VARARGS, "."}, {"set_download_rate_limit", torrent_set_download_rate_limit, METH_VARARGS, "."}, {"set_upload_rate_limit", torrent_set_upload_rate_limit, METH_VARARGS, "."}, {"set_listen_on", torrent_set_listen_on, METH_VARARGS, "."}, {"is_listening", torrent_is_listening, METH_VARARGS, "."}, {"listening_port", torrent_listening_port, METH_VARARGS, "."}, {"set_max_uploads", torrent_set_max_uploads, METH_VARARGS, "."}, {"set_max_connections", torrent_set_max_connections, METH_VARARGS, "."}, {"add_torrent", torrent_add_torrent, METH_VARARGS, "."}, {"remove_torrent", torrent_remove_torrent, METH_VARARGS, "."}, {"get_num_torrents", torrent_get_num_torrents, METH_VARARGS, "."}, {"reannounce", torrent_reannounce, METH_VARARGS, "."}, {"is_paused", torrent_is_paused, METH_VARARGS, "."}, {"pause", torrent_pause, METH_VARARGS, "."}, {"resume", torrent_resume, METH_VARARGS, "."}, {"get_torrent_info", torrent_get_torrent_info, METH_VARARGS, "."}, {"get_state", torrent_get_state, METH_VARARGS, "."}, {"pop_event", torrent_pop_event, METH_VARARGS, "."}, {"get_session_info", torrent_get_session_info, METH_VARARGS, "."}, {"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."}, /* {"get_unique_IDs", torrent_get_unique_IDs, METH_VARARGS, "."},*/ {"get_file_info", torrent_get_file_info, METH_VARARGS, "."}, {"set_filter_out", torrent_set_filter_out, METH_VARARGS, "."}, {"constants", torrent_constants, METH_VARARGS, "."}, {"start_DHT", torrent_start_DHT, METH_VARARGS, "."}, {"stop_DHT", torrent_stop_DHT, METH_VARARGS, "."}, {"get_DHT_info", torrent_get_DHT_info, METH_VARARGS, "."}, {"create_torrent", torrent_create_torrent, METH_VARARGS, "."}, {"apply_IP_filter", torrent_apply_IP_filter, METH_VARARGS, "."}, {NULL} /* Sentinel */ }; PyMODINIT_FUNC initpytorrent_core(void) { PyObject *m, *d; m = Py_InitModule("pytorrent_core", pytorrent_core_methods); PyTorrentCoreError = PyErr_NewException("pytorrent_core.Error", NULL, NULL); InvalidEncodingError = PyErr_NewException("pytorrent_core.InvalidEncodingError", NULL, NULL); FilesystemError = PyErr_NewException("pytorrent_core.FilesystemError", NULL, NULL); DuplicateTorrentError = PyErr_NewException("pytorrent_core.DuplicateTorrentError", NULL, NULL); InvalidTorrentError = PyErr_NewException("pytorrent_core.InvalidTorrentError", NULL, NULL); d = PyModule_GetDict(m); PyDict_SetItemString(d, "PyTorrentCoreError", PyTorrentCoreError); PyDict_SetItemString(d, "InvalidEncodingError", InvalidEncodingError); PyDict_SetItemString(d, "FilesystemError", FilesystemError); PyDict_SetItemString(d, "DuplicateTorrentError", DuplicateTorrentError); PyDict_SetItemString(d, "InvalidTorrentError", InvalidTorrentError); };