Ticket #3100: add-support-for-internal-torrent-viewer-vfs.patch

File add-support-for-internal-torrent-viewer-vfs.patch, 16.8 KB (added by eugenesan, 10 years ago)

support kernel initrd images as VFS

  • misc/ext.d/misc.sh.in

    From 14b13f25988be0cb21e7e7bace5fa574b869325c Mon Sep 17 00:00:00 2001
    From: "Eugene San (eugenesan)" <eugenesan@gmail.com>
    Date: Tue, 3 Dec 2013 09:58:55 +0200
    Subject: [PATCH] add support for internal torrent viewer/vfs
    
    Currently ext.d viewer for torrent depends on external ctorrent
    and currently partially broken most distributions:
    http://sourceforge.net/p/dtorrent/bugs/21/
    
    This adds viewer and vfs support based on embedded python script found here:
    http://phdru.name/Software/mc/torrent
    ---
     misc/ext.d/misc.sh.in             |   2 +-
     misc/mc.ext.in                    |   1 +
     src/vfs/extfs/helpers/Makefile.am |   2 +-
     src/vfs/extfs/helpers/torrent     | 396 ++++++++++++++++++++++++++++++++++++++
     4 files changed, 399 insertions(+), 2 deletions(-)
     create mode 100755 src/vfs/extfs/helpers/torrent
    
    diff --git a/misc/ext.d/misc.sh.in b/misc/ext.d/misc.sh.in
    index f62ba89..d249219 100644
    a b do_view_action() { 
    4545        lyxcat "${MC_EXT_FILENAME}" 
    4646        ;; 
    4747    torrent) 
    48         ctorrent -x "${MC_EXT_FILENAME}" 2>/dev/null 
     48        @EXTHELPERSDIR@/../extfs.d/torrent view "${MC_EXT_FILENAME}" 2>/dev/null 
    4949        ;; 
    5050    javaclass) 
    5151        jad -p "${MC_EXT_FILENAME}" 2>/dev/null 
  • misc/mc.ext.in

    diff --git a/misc/mc.ext.in b/misc/mc.ext.in
    index d330685..d373813 100644
    a b shell/i/.lyx 
    663663 
    664664# torrent 
    665665shell/i/.torrent 
     666        Open=%cd %p/torrent:// 
    666667        View=%view{ascii} @EXTHELPERSDIR@/misc.sh view torrent 
    667668 
    668669### Plain compressed files ### 
  • src/vfs/extfs/helpers/Makefile.am

    diff --git a/src/vfs/extfs/helpers/Makefile.am b/src/vfs/extfs/helpers/Makefile.am
    index 06ea789..5bd5e5c 100644
    a b EXTFSCONFFILES = sfs.ini 
    66EXTFS_MISC  = README README.extfs 
    77 
    88# Scripts hat don't need adaptation to the local system 
    9 EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm trpm u7z 
     9EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm torrent trpm u7z 
    1010 
    1111# Scripts that need adaptation to the local system - source files 
    1212EXTFS_IN    =                   \ 
  • new file src/vfs/extfs/helpers/torrent

    diff --git a/src/vfs/extfs/helpers/torrent b/src/vfs/extfs/helpers/torrent
    new file mode 100755
    index 0000000..e716391
    - +  
     1#! /usr/bin/env python 
     2"""Torrent Virtual FileSystem for Midnight Commander 
     3 
     4The script requires: 
     5Midnight Commander 3.1+ (http://www.midnight-commander.org/), 
     6Python 2.4+ (http://www.python.org/). 
     7 
     8Includes: eff_bdecode.py (http://effbot.org/zone/bencode.htm). 
     9 
     10For mc 4.7+ put the script in $HOME/.mc/extfs.d. 
     11For older versions put it in /usr/[local/][lib|share]/mc/extfs 
     12and add a line "torrent" to the /usr/[local/][lib|share]/mc/extfs/extfs.ini. 
     13Make the script executable. 
     14 
     15Run this "cd" command in the Midnight Commander (in the "bindings" file the 
     16command is "%cd"): cd file#torrent, where "file" is the name of your torrent 
     17metafile. The VFS lists all files and directories from the torrent metafile; 
     18all files appear empty, of course, but the sizes are shown. Filenames are 
     19reencoded from the metafile's encoding/codepage to the current locale. 
     20 
     21Along with the files/directories in the torrent metafile the VFS also presents 
     22meta information - in the form of files in .META directory. The size and 
     23contents of these files are taken from the corresponding fields in the torrent 
     24metafile. The script doesn't check if the torrent consists of a .META file or 
     25directory (quite unlikely). 
     26 
     27Date/time for all files is set to midnight of the 1st January of the current 
     28year. The filesystem is, naturally, read-only. 
     29 
     30""" 
     31 
     32__version__ = "1.1.1" 
     33__author__ = "Oleg Broytman <phd@phdru.name>" 
     34__copyright__ = "Copyright (C) 2010-2013 PhiloSoft Design" 
     35__license__ = "GPL" 
     36 
     37import locale, sys, os, re, logging 
     38 
     39logger = logging.getLogger('torrent-mcextfs') 
     40log_err_handler = logging.StreamHandler(sys.stderr) 
     41logger.addHandler(log_err_handler) 
     42logger.setLevel(logging.INFO) 
     43 
     44if len(sys.argv) < 3: 
     45    logger.critical("""\ 
     46Torrent Virtual FileSystem for Midnight Commander version %s 
     47Author: %s 
     48%s 
     49 
     50This is not a program. Put the script in /usr/[local/][lib|share]/mc/extfs. 
     51For more information read the source!""", 
     52   __version__, __author__, __copyright__ 
     53) 
     54    sys.exit(1) 
     55 
     56 
     57locale.setlocale(locale.LC_ALL, '') 
     58charset = locale.getpreferredencoding() 
     59 
     60def effbtokenize(text, match=re.compile("([idel])|(\d+):|(-?\d+)").match): 
     61    i = 0 
     62    while i < len(text): 
     63        m = match(text, i) 
     64        s = m.group(m.lastindex) 
     65        i = m.end() 
     66        if m.lastindex == 2: 
     67            yield "s" 
     68            yield text[i:i+int(s)] 
     69            i = i + int(s) 
     70        else: 
     71            yield s 
     72 
     73def effbdecode_item(next, token): 
     74    if token == "i": 
     75        # integer: "i" value "e" 
     76        data = int(next()) 
     77        if next() != "e": 
     78            raise ValueError 
     79    elif token == "s": 
     80        # string: "s" value (virtual tokens) 
     81        data = next() 
     82    elif token == "l" or token == "d": 
     83        # container: "l" (or "d") values "e" 
     84        data = [] 
     85        tok = next() 
     86        while tok != "e": 
     87            data.append(effbdecode_item(next, tok)) 
     88            tok = next() 
     89        if token == "d": 
     90            data = dict(zip(data[0::2], data[1::2])) 
     91    else: 
     92        raise ValueError 
     93    return data 
     94 
     95def effbdecode(text): 
     96    try: 
     97        src = effbtokenize(text) 
     98        data = effbdecode_item(src.next, src.next()) 
     99        for token in src: # look for more tokens 
     100            raise SyntaxError("trailing junk") 
     101    except (AttributeError, ValueError, StopIteration): 
     102        raise SyntaxError("syntax error") 
     103    return data 
     104 
     105def mctorrent_view(): 
     106    """View VFS summary""" 
     107 
     108    total_size = 0 
     109 
     110    if 'info' not in torrent: 
     111        torrent_error('Info absent') 
     112 
     113    info = torrent['info'] 
     114    if 'name' not in info and 'name.utf-8' not in info: 
     115        torrent_error('Unknown name') 
     116 
     117    codepage = torrent.get('codepage', None) 
     118    encoding = torrent.get('encoding', None) 
     119    if not encoding and codepage: 
     120        encoding = str(codepage) 
     121 
     122    name = info['name'] 
     123    name_utf8 = info.get('name.utf-8', None) 
     124 
     125    if 'files' in info: 
     126        files = info['files'] 
     127        paths = [] 
     128        for file in files: 
     129            if 'path' not in file and 'path.utf-8' not in file: 
     130                torrent_error('Unknown path') 
     131            if 'length' not in file: 
     132                torrent_error('Unknown length') 
     133            if 'path.utf-8' in file: 
     134                if name_utf8: 
     135                    path = '/'.join([name_utf8] + file['path.utf-8']) 
     136                    if charset and (charset != 'utf-8'): 
     137                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     138                else: 
     139                    _name_utf8 = name 
     140                    if encoding and (encoding != 'utf-8'): 
     141                        _name_utf8 = _name_utf8.decode(encoding, 'replace').encode('utf-8', 'replace') 
     142                    path = '/'.join([_name_utf8] + file['path.utf-8']) 
     143                    if charset and (charset != 'utf-8'): 
     144                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     145            else: 
     146                if name_utf8: 
     147                    path = file['path'] 
     148                    if encoding and (encoding != 'utf-8'): 
     149                        path = path.decode(encoding, 'replace').encode('utf-8', 'replace') 
     150                    path = '/'.join([name_utf8] + path) 
     151                    if charset and (charset != 'utf-8'): 
     152                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     153                else: 
     154                    path = '/'.join([name] + file['path']) 
     155                    if charset and encoding and (charset != encoding): 
     156                        path = path.decode(encoding, 'replace').encode(charset, 'replace') 
     157            total_size += file['length'] 
     158            print "%s %db" % (path, file['length']) 
     159    else: # One-file torrent 
     160        if 'length' not in info: 
     161            torrent_error('Unknown length') 
     162        if name_utf8: 
     163            if charset and (charset != 'utf-8'): 
     164                name = name_utf8.decode('utf-8', 'replace').encode(charset, 'replace') 
     165        elif charset and encoding and (charset != encoding): 
     166            name = name.decode(encoding, 'replace').encode(charset, 'replace') 
     167 
     168        total_size += info['length'] 
     169        print "%s %db" % (name, info['length']) 
     170 
     171    for name in 'announce', 'announce-list', 'codepage', 'comment', \ 
     172                'created by', 'creation date', 'encoding', \ 
     173                'nodes', 'publisher', 'publisher-url': 
     174        if name == 'comment' and 'comment.utf-8' in torrent: 
     175            data = torrent['comment.utf-8'].decode('utf-8').encode(charset, 'replace') 
     176        elif name in torrent: 
     177            if name == 'announce-list': 
     178                data = decode_announce_list(torrent[name]) 
     179            elif name == 'codepage': 
     180                data = str(torrent[name]) 
     181            elif name == 'creation date': 
     182                data = decode_datetime(torrent[name]) 
     183            elif name == 'nodes': 
     184                data = ['%s:%s' % (host, port) for host, port in torrent[name]] 
     185                data = '\n'.join(data) 
     186            else: 
     187                data = torrent[name] 
     188 
     189        print "%s: %s" % (name, data.replace("\n"," ")) 
     190 
     191    if 'private' in info: 
     192        print "Private: yes" 
     193 
     194    if 'piece length' in info: 
     195        print "piece length: %s" % str(info['piece length']) 
     196 
     197    print "Total size: %d" % total_size 
     198 
     199def mctorrent_list(): 
     200    """List the entire VFS""" 
     201 
     202    if 'info' not in torrent: 
     203        torrent_error('Info absent') 
     204 
     205    info = torrent['info'] 
     206    if 'name' not in info and 'name.utf-8' not in info: 
     207        torrent_error('Unknown name') 
     208 
     209    codepage = torrent.get('codepage', None) 
     210    encoding = torrent.get('encoding', None) 
     211    if not encoding and codepage: 
     212        encoding = str(codepage) 
     213 
     214    name = info['name'] 
     215    name_utf8 = info.get('name.utf-8', None) 
     216 
     217    if 'files' in info: 
     218        files = info['files'] 
     219        paths = [] 
     220        for file in files: 
     221            if 'path' not in file and 'path.utf-8' not in file: 
     222                torrent_error('Unknown path') 
     223            if 'length' not in file: 
     224                torrent_error('Unknown length') 
     225            if 'path.utf-8' in file: 
     226                if name_utf8: 
     227                    path = '/'.join([name_utf8] + file['path.utf-8']) 
     228                    if charset and (charset != 'utf-8'): 
     229                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     230                else: 
     231                    _name_utf8 = name 
     232                    if encoding and (encoding != 'utf-8'): 
     233                        _name_utf8 = _name_utf8.decode(encoding, 'replace').encode('utf-8', 'replace') 
     234                    path = '/'.join([_name_utf8] + file['path.utf-8']) 
     235                    if charset and (charset != 'utf-8'): 
     236                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     237            else: 
     238                if name_utf8: 
     239                    path = file['path'] 
     240                    if encoding and (encoding != 'utf-8'): 
     241                        path = path.decode(encoding, 'replace').encode('utf-8', 'replace') 
     242                    path = '/'.join([name_utf8] + path) 
     243                    if charset and (charset != 'utf-8'): 
     244                        path = path.decode('utf-8', 'replace').encode(charset, 'replace') 
     245                else: 
     246                    path = '/'.join([name] + file['path']) 
     247                    if charset and encoding and (charset != encoding): 
     248                        path = path.decode(encoding, 'replace').encode(charset, 'replace') 
     249            length = file['length'] 
     250            paths.append((path, length)) 
     251    else: # One-file torrent 
     252        if 'length' not in info: 
     253            torrent_error('Unknown length') 
     254        length = info['length'] 
     255        if name_utf8: 
     256            if charset and (charset != 'utf-8'): 
     257                name = name_utf8.decode('utf-8', 'replace').encode(charset, 'replace') 
     258        elif charset and encoding and (charset != encoding): 
     259            name = name.decode(encoding, 'replace').encode(charset, 'replace') 
     260        paths = [(name, length)] 
     261 
     262    meta = [] 
     263    for name in 'announce', 'announce-list', 'codepage', 'comment', \ 
     264                'created by', 'creation date', 'encoding', \ 
     265                'nodes', 'publisher', 'publisher-url': 
     266        if name == 'comment' and 'comment.utf-8' in torrent: 
     267            data = torrent['comment.utf-8'].decode('utf-8').encode(charset, 'replace') 
     268            meta.append(('.META/' + name, len(data))) 
     269        elif name in torrent: 
     270            if name == 'announce-list': 
     271                data = decode_announce_list(torrent[name]) 
     272            elif name == 'codepage': 
     273                data = str(torrent[name]) 
     274            elif name == 'creation date': 
     275                data = decode_datetime(torrent[name]) 
     276            elif name == 'nodes': 
     277                data = ['%s:%s' % (host, port) for host, port in torrent[name]] 
     278                data = '\n'.join(data) 
     279            else: 
     280                data = torrent[name] 
     281            meta.append(('.META/' + name, len(data))) 
     282 
     283    if 'private' in info: 
     284        meta.append(('.META/private', 1)) 
     285 
     286    if 'piece length' in info: 
     287        meta.append(('.META/piece length', len(str(info['piece length'])))) 
     288 
     289    for name, size in paths + meta: 
     290        print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name) 
     291 
     292def mctorrent_copyout(): 
     293    """Extract a file from the VFS""" 
     294 
     295    torrent_filename = sys.argv[3] 
     296    real_filename = sys.argv[4] 
     297    data = None 
     298 
     299    for name in 'announce', 'announce-list', 'codepage', 'comment', \ 
     300                'created by', 'creation date', 'encoding', \ 
     301                'nodes', 'publisher', 'publisher-url': 
     302        if name == 'comment' and 'comment.utf-8' in torrent: 
     303            data = torrent['comment.utf-8'].decode('utf-8').encode(charset, 'replace') 
     304            meta.append(('.META/' + name, len(data))) 
     305        elif torrent_filename == '.META/' + name: 
     306            if name in torrent: 
     307                if name == 'announce-list': 
     308                    data = decode_announce_list(torrent[name]) 
     309                elif name == 'codepage': 
     310                    data = str(torrent[name]) 
     311                elif name == 'creation date': 
     312                    data = decode_datetime(torrent[name]) 
     313                elif name == 'nodes': 
     314                    data = ['%s:%s' % (host, port) for host, port in torrent[name]] 
     315                    data = '\n'.join(data) 
     316                else: 
     317                    data = str(torrent[name]) 
     318            else: 
     319                torrent_error('Unknown ' + name) 
     320            break 
     321 
     322    if torrent_filename in ('.META/private', '.META/piece length'): 
     323        if 'info' not in torrent: 
     324            torrent_error('Info absent') 
     325        info = torrent['info'] 
     326        if torrent_filename == '.META/private': 
     327            if 'private' not in info: 
     328                torrent_error('Info absent') 
     329        if torrent_filename == '.META/piece length': 
     330            if 'piece length' not in info: 
     331                torrent_error('Info absent') 
     332        data = str(info[torrent_filename[len('.META/'):]]) 
     333 
     334    if not torrent_filename.startswith('.META/'): 
     335        data = '' 
     336 
     337    if data is None: 
     338        torrent_error('Unknown file name') 
     339    else: 
     340        outfile = open(real_filename, 'w') 
     341        outfile.write(data) 
     342        outfile.close() 
     343 
     344def mctorrent_copyin(): 
     345    """Put a file to the VFS""" 
     346    sys.exit("Torrent VFS doesn't support adding files (read-only filesystem)") 
     347 
     348def mctorrent_rm(): 
     349    """Remove a file from the VFS""" 
     350    sys.exit("Torrent VFS doesn't support removing files/directories (read-only filesystem)") 
     351 
     352mctorrent_rmdir = mctorrent_rm 
     353 
     354def mctorrent_mkdir(): 
     355    """Create a directory in the VFS""" 
     356    sys.exit("Torrent VFS doesn't support creating directories (read-only filesystem)") 
     357 
     358def torrent_error(error_str): 
     359    logger.critical("Error parsing the torrent metafile: %s", error_str) 
     360    sys.exit(1) 
     361 
     362def decode_torrent(): 
     363    try: 
     364        torrent_file = open(sys.argv[2], 'r') 
     365        data = torrent_file.read() 
     366        torrent_file.close() 
     367        return effbdecode(data) 
     368    except IOError, error_str: 
     369        torrent_error(error_str) 
     370 
     371def decode_datetime(dt): 
     372    from time import localtime, asctime 
     373    the_time = float(dt) 
     374    l_now = localtime(the_time) 
     375    return asctime(l_now) 
     376 
     377def decode_announce_list(announce): 
     378    return '\n'.join(l[0] for l in announce) 
     379 
     380 
     381command = sys.argv[1] 
     382procname = "mctorrent_" + command 
     383 
     384g = globals() 
     385if not g.has_key(procname): 
     386    logger.critical("Unknown command %s", command) 
     387    sys.exit(1) 
     388 
     389torrent = decode_torrent() 
     390 
     391try: 
     392    g[procname]() 
     393except SystemExit: 
     394    raise 
     395except: 
     396    logger.exception("Error during run")