~cmiller/ubuntu/quantal/deluge/fix-parameter-move-storage

« back to all changes in this revision

Viewing changes to libtorrent/src/ut_metadata.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cristian Greco
  • Date: 2009-11-13 02:39:45 UTC
  • mfrom: (4.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091113023945-te1bybo2912ejzuc
Tags: 1.2.0~rc3-4
* debian/control: bump build-dep on python-setuptools to (>= 0.6c9).
* debian/patches:
  - 25_r5921_fastresume_files.patch
    new, should fix problems with fresh configs;
  - 30_r5931_ipc_lockfile.patch:
    new, should fix an issue where Deluge will fail to start if there is a
    stale ipc lockfile. (Closes: #555849)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 
3
 
Copyright (c) 2007, Arvid Norberg
4
 
All rights reserved.
5
 
 
6
 
Redistribution and use in source and binary forms, with or without
7
 
modification, are permitted provided that the following conditions
8
 
are met:
9
 
 
10
 
    * Redistributions of source code must retain the above copyright
11
 
      notice, this list of conditions and the following disclaimer.
12
 
    * Redistributions in binary form must reproduce the above copyright
13
 
      notice, this list of conditions and the following disclaimer in
14
 
      the documentation and/or other materials provided with the distribution.
15
 
    * Neither the name of the author nor the names of its
16
 
      contributors may be used to endorse or promote products derived
17
 
      from this software without specific prior written permission.
18
 
 
19
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
 
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 
POSSIBILITY OF SUCH DAMAGE.
30
 
 
31
 
*/
32
 
 
33
 
#include "libtorrent/pch.hpp"
34
 
 
35
 
#ifdef _MSC_VER
36
 
#pragma warning(push, 1)
37
 
#endif
38
 
 
39
 
#include <boost/shared_ptr.hpp>
40
 
 
41
 
#ifdef _MSC_VER
42
 
#pragma warning(pop)
43
 
#endif
44
 
 
45
 
#include <vector>
46
 
#include <utility>
47
 
#include <numeric>
48
 
#include <cstdio>
49
 
 
50
 
#include "libtorrent/peer_connection.hpp"
51
 
#include "libtorrent/bt_peer_connection.hpp"
52
 
#include "libtorrent/hasher.hpp"
53
 
#include "libtorrent/bencode.hpp"
54
 
#include "libtorrent/torrent.hpp"
55
 
#include "libtorrent/extensions.hpp"
56
 
#include "libtorrent/extensions/ut_metadata.hpp"
57
 
#include "libtorrent/alert_types.hpp"
58
 
#ifdef TORRENT_STATS
59
 
#include "libtorrent/aux_/session_impl.hpp"
60
 
#endif
61
 
 
62
 
namespace libtorrent { namespace
63
 
{
64
 
        int div_round_up(int numerator, int denominator)
65
 
        {
66
 
                return (numerator + denominator - 1) / denominator;
67
 
        }
68
 
 
69
 
        void nop(char*) {}
70
 
 
71
 
        struct ut_metadata_plugin : torrent_plugin
72
 
        {
73
 
                ut_metadata_plugin(torrent& t)
74
 
                        : m_torrent(t)
75
 
                        , m_metadata_progress(0)
76
 
                        , m_metadata_size(0)
77
 
                {
78
 
                }
79
 
 
80
 
                virtual void on_files_checked()
81
 
                {
82
 
                        // if the torrent is a seed, copy the metadata from
83
 
                        // the torrent before it is deallocated
84
 
                        if (m_torrent.is_seed())
85
 
                                metadata();
86
 
                }
87
 
 
88
 
                virtual boost::shared_ptr<peer_plugin> new_connection(
89
 
                        peer_connection* pc);
90
 
                
91
 
                buffer::const_interval metadata() const
92
 
                {
93
 
                        TORRENT_ASSERT(m_torrent.valid_metadata());
94
 
                        if (!m_metadata)
95
 
                        {
96
 
                                m_metadata = m_torrent.torrent_file().metadata();
97
 
                                m_metadata_size = m_torrent.torrent_file().metadata_size();
98
 
                                TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final()
99
 
                                        == m_torrent.torrent_file().info_hash());
100
 
                        }
101
 
                        return buffer::const_interval(m_metadata.get(), m_metadata.get()
102
 
                                + m_metadata_size);
103
 
                }
104
 
 
105
 
                bool received_metadata(char const* buf, int size, int piece, int total_size)
106
 
                {
107
 
                        if (m_torrent.valid_metadata()) return false;
108
 
                        
109
 
                        if (!m_metadata)
110
 
                        {
111
 
                                // verify the total_size
112
 
                                if (total_size <= 0 || total_size > 500 * 1024) return false;
113
 
 
114
 
                                m_metadata.reset(new char[total_size]);
115
 
                                m_requested_metadata.resize(div_round_up(total_size, 16 * 1024), 0);
116
 
                                m_metadata_size = total_size;
117
 
                        }
118
 
 
119
 
                        if (piece < 0 || piece >= int(m_requested_metadata.size()))
120
 
                                return false;
121
 
 
122
 
                        TORRENT_ASSERT(piece * 16 * 1024 + size <= m_metadata_size);
123
 
                        std::memcpy(&m_metadata[piece * 16 * 1024], buf, size);
124
 
                        // mark this piece has 'have'
125
 
                        m_requested_metadata[piece] = (std::numeric_limits<int>::max)();
126
 
 
127
 
                        bool have_all = std::count(m_requested_metadata.begin()
128
 
                                , m_requested_metadata.end(), (std::numeric_limits<int>::max)())
129
 
                                == int(m_requested_metadata.size());
130
 
 
131
 
                        if (!have_all) return false;
132
 
 
133
 
                        hasher h;
134
 
                        h.update(&m_metadata[0], m_metadata_size);
135
 
                        sha1_hash info_hash = h.final();
136
 
 
137
 
                        if (info_hash != m_torrent.torrent_file().info_hash())
138
 
                        {
139
 
                                std::fill(m_requested_metadata.begin(), m_requested_metadata.end(), 0);
140
 
 
141
 
                                if (m_torrent.alerts().should_post<metadata_failed_alert>())
142
 
                                {
143
 
                                        m_torrent.alerts().post_alert(metadata_failed_alert(
144
 
                                                m_torrent.get_handle()));
145
 
                                }
146
 
 
147
 
                                return false;
148
 
                        }
149
 
 
150
 
                        lazy_entry metadata;
151
 
                        int ret = lazy_bdecode(m_metadata.get(), m_metadata.get() + m_metadata_size, metadata);
152
 
                        std::string error;
153
 
                        if (!m_torrent.set_metadata(metadata, error))
154
 
                        {
155
 
                                // this means the metadata is correct, since we
156
 
                                // verified it against the info-hash, but we
157
 
                                // failed to parse it. Pause the torrent
158
 
                                // TODO: Post an alert!
159
 
                                m_torrent.pause();
160
 
                                return false;
161
 
                        }
162
 
 
163
 
                        // clear the storage for the bitfield
164
 
                        std::vector<int>().swap(m_requested_metadata);
165
 
 
166
 
                        return true;
167
 
                }
168
 
 
169
 
                // returns a piece of the metadata that
170
 
                // we should request.
171
 
                int metadata_request();
172
 
 
173
 
                // this is called from the peer_connection for
174
 
                // each piece of metadata it receives
175
 
                void metadata_progress(int total_size, int received)
176
 
                {
177
 
                        m_metadata_progress += received;
178
 
                        m_metadata_size = total_size;
179
 
                }
180
 
 
181
 
                void on_piece_pass(int)
182
 
                {
183
 
                        // if we became a seed, copy the metadata from
184
 
                        // the torrent before it is deallocated
185
 
                        if (m_torrent.is_seed())
186
 
                                metadata();
187
 
                }
188
 
 
189
 
                void metadata_size(int size)
190
 
                {
191
 
                        if (m_metadata_size > 0 || size <= 0 || size > 500 * 1024) return;
192
 
                        m_metadata_size = size;
193
 
                        m_metadata.reset(new char[size]);
194
 
                        m_requested_metadata.resize(div_round_up(size, 16 * 1024), 0);
195
 
                }
196
 
 
197
 
        private:
198
 
                torrent& m_torrent;
199
 
 
200
 
                // this buffer is filled with the info-section of
201
 
                // the metadata file while downloading it from
202
 
                // peers, and while sending it.
203
 
                // it is mutable because it's generated lazily
204
 
                mutable boost::shared_array<char> m_metadata;
205
 
 
206
 
                int m_metadata_progress;
207
 
                mutable int m_metadata_size;
208
 
 
209
 
                // this vector keeps track of how many times each meatdata
210
 
                // block has been requested
211
 
                // std::numeric_limits<int>::max() means we have the piece
212
 
                std::vector<int> m_requested_metadata;
213
 
        };
214
 
 
215
 
 
216
 
        struct ut_metadata_peer_plugin : peer_plugin
217
 
        {
218
 
                ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc
219
 
                        , ut_metadata_plugin& tp)
220
 
                        : m_message_index(0)
221
 
                        , m_no_metadata(min_time())
222
 
                        , m_torrent(t)
223
 
                        , m_pc(pc)
224
 
                        , m_tp(tp)
225
 
                {}
226
 
 
227
 
                // can add entries to the extension handshake
228
 
                virtual void add_handshake(entry& h)
229
 
                {
230
 
                        entry& messages = h["m"];
231
 
                        messages["ut_metadata"] = 15;
232
 
                        if (m_torrent.valid_metadata())
233
 
                                h["metadata_size"] = m_tp.metadata().left();
234
 
                }
235
 
 
236
 
                // called when the extension handshake from the other end is received
237
 
                virtual bool on_extension_handshake(lazy_entry const& h)
238
 
                {
239
 
                        m_message_index = 0;
240
 
                        if (h.type() != lazy_entry::dict_t) return false;
241
 
                        lazy_entry const* messages = h.dict_find("m");
242
 
                        if (!messages || messages->type() != lazy_entry::dict_t) return false;
243
 
 
244
 
                        int index = messages->dict_find_int_value("ut_metadata", -1);
245
 
                        if (index == -1) return false;
246
 
                        m_message_index = index;
247
 
 
248
 
                        int metadata_size = h.dict_find_int_value("metadata_size");
249
 
                        if (metadata_size > 0)
250
 
                                m_tp.metadata_size(metadata_size);
251
 
                        return true;
252
 
                }
253
 
 
254
 
                void write_metadata_packet(int type, int piece)
255
 
                {
256
 
                        TORRENT_ASSERT(type >= 0 && type <= 2);
257
 
                        TORRENT_ASSERT(piece >= 0);
258
 
                        TORRENT_ASSERT(!m_pc.associated_torrent().expired());
259
 
 
260
 
#ifdef TORRENT_VERBOSE_LOGGING
261
 
                        (*m_pc.m_logger) << time_now_string() << " ==> UT_METADATA [ "
262
 
                                "type: " << type << " | piece: " << piece << " ]\n";
263
 
#endif
264
 
                        // abort if the peer doesn't support the metadata extension
265
 
                        if (m_message_index == 0) return;
266
 
 
267
 
                        entry e;
268
 
                        e["msg_type"] = type;
269
 
                        e["piece"] = piece;
270
 
 
271
 
                        char const* metadata = 0;
272
 
                        int metadata_piece_size = 0;
273
 
 
274
 
                        if (type == 1)
275
 
                        {
276
 
                                TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata());
277
 
                                e["total_size"] = m_tp.metadata().left();
278
 
                                int offset = piece * 16 * 1024;
279
 
                                metadata = m_tp.metadata().begin + offset;
280
 
                                metadata_piece_size = (std::min)(
281
 
                                        int(m_tp.metadata().left() - offset), 16 * 1024);
282
 
                                TORRENT_ASSERT(metadata_piece_size > 0);
283
 
                                TORRENT_ASSERT(offset >= 0);
284
 
                                TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.metadata().left()));
285
 
                        }
286
 
 
287
 
                        char msg[200];
288
 
                        char* header = msg;
289
 
                        char* p = &msg[6];
290
 
                        int len = bencode(p, e);
291
 
                        int total_size = 2 + len + metadata_piece_size;
292
 
                        namespace io = detail;
293
 
                        io::write_uint32(total_size, header);
294
 
                        io::write_uint8(bt_peer_connection::msg_extended, header);
295
 
                        io::write_uint8(m_message_index, header);
296
 
 
297
 
                        m_pc.send_buffer(msg, len + 6);
298
 
                        if (metadata_piece_size) m_pc.append_send_buffer(
299
 
                                (char*)metadata, metadata_piece_size, &nop);
300
 
                }
301
 
 
302
 
                virtual bool on_extended(int length
303
 
                        , int extended_msg, buffer::const_interval body)
304
 
                {
305
 
                        if (extended_msg != 15) return false;
306
 
                        if (m_message_index == 0) return false;
307
 
 
308
 
                        if (length > 17 * 1024)
309
 
                        {
310
 
                                m_pc.disconnect("ut_metadata message larger than 17 kB", 2);
311
 
                                return true;
312
 
                        }
313
 
 
314
 
                        if (!m_pc.packet_finished()) return true;
315
 
 
316
 
                        int len;
317
 
                        entry msg = bdecode(body.begin, body.end, len);
318
 
                        if (msg.type() == entry::undefined_t)
319
 
                        {
320
 
                                m_pc.disconnect("invalid bencoding in ut_metadata message", 2);
321
 
                                return true;
322
 
                        }
323
 
 
324
 
                        int type = msg["msg_type"].integer();
325
 
                        int piece = msg["piece"].integer();
326
 
 
327
 
#ifdef TORRENT_VERBOSE_LOGGING
328
 
                        (*m_pc.m_logger) << time_now_string() << " <== UT_METADATA [ "
329
 
                                "type: " << type << " | piece: " << piece << " ]\n";
330
 
#endif
331
 
 
332
 
                        switch (type)
333
 
                        {
334
 
                        case 0: // request
335
 
                                {
336
 
                                        if (!m_torrent.valid_metadata())
337
 
                                        {
338
 
                                                write_metadata_packet(2, piece);
339
 
                                                return true;
340
 
                                        }
341
 
                                        // TODO: put the request on the queue in some cases
342
 
                                        write_metadata_packet(1, piece);
343
 
                                }
344
 
                                break;
345
 
                        case 1: // data
346
 
                                {
347
 
                                        std::vector<int>::iterator i = std::find(m_sent_requests.begin()
348
 
                                                , m_sent_requests.end(), piece);
349
 
 
350
 
                                        // unwanted piece?
351
 
                                        if (i == m_sent_requests.end()) return true;
352
 
 
353
 
                                        m_sent_requests.erase(i);
354
 
                                        entry const* total_size = msg.find_key("total_size");
355
 
                                        m_tp.received_metadata(body.begin + len, body.left() - len, piece
356
 
                                                , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0);
357
 
                                }
358
 
                                break;
359
 
                        case 2: // have no data
360
 
                                {
361
 
                                        m_no_metadata = time_now();
362
 
                                        std::vector<int>::iterator i = std::find(m_sent_requests.begin()
363
 
                                                , m_sent_requests.end(), piece);
364
 
                                        // unwanted piece?
365
 
                                        if (i == m_sent_requests.end()) return true;
366
 
                                        m_sent_requests.erase(i);
367
 
                                }
368
 
                                break;
369
 
                        default:
370
 
                                {
371
 
                                        std::stringstream msg;
372
 
                                        msg << "unknown ut_metadata extension message: " << type;
373
 
                                        m_pc.disconnect(msg.str().c_str(), 2);
374
 
                                }
375
 
                        }
376
 
                        return true;
377
 
                }
378
 
 
379
 
                virtual void tick()
380
 
                {
381
 
                        // if we don't have any metadata, and this peer
382
 
                        // supports the request metadata extension
383
 
                        // and we aren't currently waiting for a request
384
 
                        // reply. Then, send a request for some metadata.
385
 
                        if (!m_torrent.valid_metadata()
386
 
                                && m_message_index != 0
387
 
                                && m_sent_requests.size() < 2
388
 
                                && has_metadata())
389
 
                        {
390
 
                                int piece = m_tp.metadata_request();
391
 
                                m_sent_requests.push_back(piece);
392
 
                                write_metadata_packet(0, piece);
393
 
                        }
394
 
                }
395
 
 
396
 
                bool has_metadata() const
397
 
                {
398
 
                        return time_now() - m_no_metadata > minutes(1);
399
 
                }
400
 
 
401
 
        private:
402
 
 
403
 
                // this is the message index the remote peer uses
404
 
                // for metadata extension messages.
405
 
                int m_message_index;
406
 
 
407
 
                // this is set to the current time each time we get a
408
 
                // "I don't have metadata" message.
409
 
                ptime m_no_metadata;
410
 
 
411
 
                // request queues
412
 
                std::vector<int> m_sent_requests;
413
 
                std::vector<int> m_incoming_requests;
414
 
                
415
 
                torrent& m_torrent;
416
 
                bt_peer_connection& m_pc;
417
 
                ut_metadata_plugin& m_tp;
418
 
        };
419
 
 
420
 
        boost::shared_ptr<peer_plugin> ut_metadata_plugin::new_connection(
421
 
                peer_connection* pc)
422
 
        {
423
 
                bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
424
 
                if (!c) return boost::shared_ptr<peer_plugin>();
425
 
                return boost::shared_ptr<peer_plugin>(new ut_metadata_peer_plugin(m_torrent, *c, *this));
426
 
        }
427
 
 
428
 
        int ut_metadata_plugin::metadata_request()
429
 
        {
430
 
                std::vector<int>::iterator i = std::min_element(
431
 
                        m_requested_metadata.begin(), m_requested_metadata.end());
432
 
 
433
 
                if (m_requested_metadata.empty())
434
 
                {
435
 
                        // if we don't know how many pieces there are
436
 
                        // just ask for piece 0
437
 
                        m_requested_metadata.resize(1, 1);
438
 
                        return 0;
439
 
                }
440
 
 
441
 
                int piece = i - m_requested_metadata.begin();
442
 
                m_requested_metadata[piece] = piece;
443
 
                return piece;
444
 
        }
445
 
 
446
 
} }
447
 
 
448
 
namespace libtorrent
449
 
{
450
 
 
451
 
        boost::shared_ptr<torrent_plugin> create_ut_metadata_plugin(torrent* t, void*)
452
 
        {
453
 
                // don't add this extension if the torrent is private
454
 
                if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr<torrent_plugin>();
455
 
                return boost::shared_ptr<torrent_plugin>(new ut_metadata_plugin(*t));
456
 
        }
457
 
 
458
 
}
459
 
 
460