summaryrefslogtreecommitdiffstats
path: root/BitTorrent/makemetafile.py
diff options
context:
space:
mode:
Diffstat (limited to 'BitTorrent/makemetafile.py')
-rw-r--r--BitTorrent/makemetafile.py263
1 files changed, 263 insertions, 0 deletions
diff --git a/BitTorrent/makemetafile.py b/BitTorrent/makemetafile.py
new file mode 100644
index 0000000..35aee66
--- /dev/null
+++ b/BitTorrent/makemetafile.py
@@ -0,0 +1,263 @@
+#!/usr/bin/env python
+
+# The contents of this file are subject to the BitTorrent Open Source License
+# Version 1.1 (the License). You may not copy or use this file, in either
+# source code or executable form, except in compliance with the License. You
+# may obtain a copy of the License at http://www.bittorrent.com/license/.
+#
+# Software distributed under the License is distributed on an AS IS basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+
+# Written by Bram Cohen
+
+from __future__ import division
+
+import os
+import sys
+from sha import sha
+from time import time
+from threading import Event
+
+from BitTorrent.bencode import bencode, bdecode
+from BitTorrent.btformats import check_info
+from BitTorrent.parseargs import parseargs, printHelp
+from BitTorrent.obsoletepythonsupport import *
+from BitTorrent import BTFailure
+
+from khashmir.node import Node
+from khashmir.ktable import KTable
+from khashmir.util import packPeers, compact_peer_info
+
+ignore = ['core', 'CVS', 'Thumbs.db', 'desktop.ini']
+
+noncharacter_translate = {}
+for i in range(0xD800, 0xE000):
+ noncharacter_translate[i] = None
+for i in range(0xFDD0, 0xFDF0):
+ noncharacter_translate[i] = None
+for i in (0xFFFE, 0xFFFF):
+ noncharacter_translate[i] = None
+
+del i
+
+def dummy(v):
+ pass
+
+def make_meta_files(url,
+ files,
+ flag=Event(),
+ progressfunc=dummy,
+ filefunc=dummy,
+ piece_len_pow2=None,
+ target=None,
+ comment=None,
+ filesystem_encoding=None,
+ use_tracker=True,
+ data_dir = None):
+ if len(files) > 1 and target:
+ raise BTFailure(_("You can't specify the name of the .torrent file "
+ "when generating multiple torrents at once"))
+
+ if not filesystem_encoding:
+ try:
+ getattr(sys, 'getfilesystemencoding')
+ except AttributeError:
+ pass
+ else:
+ filesystem_encoding = sys.getfilesystemencoding()
+ if not filesystem_encoding:
+ filesystem_encoding = 'ascii'
+ try:
+ 'a1'.decode(filesystem_encoding)
+ except:
+ raise BTFailure(_('Filesystem encoding "%s" is not supported in this version')
+ % filesystem_encoding)
+ files.sort()
+ ext = '.torrent'
+
+ togen = []
+ for f in files:
+ if not f.endswith(ext):
+ togen.append(f)
+
+ total = 0
+ for f in togen:
+ total += calcsize(f)
+
+ subtotal = [0]
+ def callback(x):
+ subtotal[0] += x
+ progressfunc(subtotal[0] / total)
+ for f in togen:
+ if flag.isSet():
+ break
+ t = os.path.split(f)
+ if t[1] == '':
+ f = t[0]
+ filefunc(f)
+ if use_tracker:
+ make_meta_file(f, url, flag=flag, progress=callback,
+ piece_len_exp=piece_len_pow2, target=target,
+ comment=comment, encoding=filesystem_encoding)
+ else:
+ make_meta_file_dht(f, url, flag=flag, progress=callback,
+ piece_len_exp=piece_len_pow2, target=target,
+ comment=comment, encoding=filesystem_encoding, data_dir=data_dir)
+
+
+def make_meta_file(path, url, piece_len_exp, flag=Event(), progress=dummy,
+ comment=None, target=None, encoding='ascii'):
+ data = {'announce': url.strip(),'creation date': int(time())}
+ piece_length = 2 ** piece_len_exp
+ a, b = os.path.split(path)
+ if not target:
+ if b == '':
+ f = a + '.torrent'
+ else:
+ f = os.path.join(a, b + '.torrent')
+ else:
+ f = target
+ info = makeinfo(path, piece_length, flag, progress, encoding)
+ if flag.isSet():
+ return
+ check_info(info)
+ h = file(f, 'wb')
+
+ data['info'] = info
+ if comment:
+ data['comment'] = comment
+ h.write(bencode(data))
+ h.close()
+
+def make_meta_file_dht(path, nodes, piece_len_exp, flag=Event(), progress=dummy,
+ comment=None, target=None, encoding='ascii', data_dir=None):
+ # if nodes is empty, then get them out of the routing table in data_dir
+ # else, expect nodes to be a string of comma seperated <ip>:<port> pairs
+ # this has a lot of duplicated code from make_meta_file
+ piece_length = 2 ** piece_len_exp
+ a, b = os.path.split(path)
+ if not target:
+ if b == '':
+ f = a + '.torrent'
+ else:
+ f = os.path.join(a, b + '.torrent')
+ else:
+ f = target
+ info = makeinfo(path, piece_length, flag, progress, encoding)
+ if flag.isSet():
+ return
+ check_info(info)
+ info_hash = sha(bencode(info)).digest()
+
+ if not nodes:
+ x = open(os.path.join(data_dir, 'routing_table'), 'rb')
+ d = bdecode(x.read())
+ x.close()
+ t = KTable(Node().initWithDict({'id':d['id'], 'host':'127.0.0.1','port': 0}))
+ for n in d['rt']:
+ t.insertNode(Node().initWithDict(n))
+ nodes = [(node.host, node.port) for node in t.findNodes(info_hash) if node.host != '127.0.0.1']
+ else:
+ nodes = [(a[0], int(a[1])) for a in [node.strip().split(":") for node in nodes.split(",")]]
+ data = {'nodes': nodes, 'creation date': int(time())}
+ h = file(f, 'wb')
+
+ data['info'] = info
+ if comment:
+ data['comment'] = comment
+ h.write(bencode(data))
+ h.close()
+
+
+def calcsize(path):
+ total = 0
+ for s in subfiles(os.path.abspath(path)):
+ total += os.path.getsize(s[1])
+ return total
+
+def makeinfo(path, piece_length, flag, progress, encoding):
+ def to_utf8(name):
+ try:
+ u = name.decode(encoding)
+ except Exception, e:
+ raise BTFailure(_('Could not convert file/directory name "%s" to '
+ 'utf-8 (%s). Either the assumed filesystem '
+ 'encoding "%s" is wrong or the filename contains '
+ 'illegal bytes.') % (name, str(e), encoding))
+ if u.translate(noncharacter_translate) != u:
+ raise BTFailure(_('File/directory name "%s" contains reserved '
+ 'unicode values that do not correspond to '
+ 'characters.') % name)
+ return u.encode('utf-8')
+ path = os.path.abspath(path)
+ if os.path.isdir(path):
+ subs = subfiles(path)
+ subs.sort()
+ pieces = []
+ sh = sha()
+ done = 0
+ fs = []
+ totalsize = 0.0
+ totalhashed = 0
+ for p, f in subs:
+ totalsize += os.path.getsize(f)
+
+ for p, f in subs:
+ pos = 0
+ size = os.path.getsize(f)
+ p2 = [to_utf8(name) for name in p]
+ fs.append({'length': size, 'path': p2})
+ h = file(f, 'rb')
+ while pos < size:
+ a = min(size - pos, piece_length - done)
+ sh.update(h.read(a))
+ if flag.isSet():
+ return
+ done += a
+ pos += a
+ totalhashed += a
+
+ if done == piece_length:
+ pieces.append(sh.digest())
+ done = 0
+ sh = sha()
+ progress(a)
+ h.close()
+ if done > 0:
+ pieces.append(sh.digest())
+ return {'pieces': ''.join(pieces),
+ 'piece length': piece_length, 'files': fs,
+ 'name': to_utf8(os.path.split(path)[1])}
+ else:
+ size = os.path.getsize(path)
+ pieces = []
+ p = 0
+ h = file(path, 'rb')
+ while p < size:
+ x = h.read(min(piece_length, size - p))
+ if flag.isSet():
+ return
+ pieces.append(sha(x).digest())
+ p += piece_length
+ if p > size:
+ p = size
+ progress(min(piece_length, size - p))
+ h.close()
+ return {'pieces': ''.join(pieces),
+ 'piece length': piece_length, 'length': size,
+ 'name': to_utf8(os.path.split(path)[1])}
+
+def subfiles(d):
+ r = []
+ stack = [([], d)]
+ while stack:
+ p, n = stack.pop()
+ if os.path.isdir(n):
+ for s in os.listdir(n):
+ if s not in ignore and not s.startswith('.'):
+ stack.append((p + [s], os.path.join(n, s)))
+ else:
+ r.append((p, n))
+ return r