ftp resume support..
fak yu file transfer protocol. special thanks goes to urllib and urlgrabber..
This commit is contained in:
+115
-4
@@ -17,9 +17,14 @@ knows what."""
|
|||||||
|
|
||||||
# python standard library modules
|
# python standard library modules
|
||||||
import urllib2
|
import urllib2
|
||||||
|
import urllib
|
||||||
|
import ftplib
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
from mimetypes import guess_type
|
||||||
|
from mimetools import Message
|
||||||
from base64 import encodestring
|
from base64 import encodestring
|
||||||
from urllib import addinfourl
|
|
||||||
from shutil import move
|
from shutil import move
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
@@ -32,7 +37,6 @@ import pisi.util as util
|
|||||||
import pisi.context as ctx
|
import pisi.context as ctx
|
||||||
from pisi.uri import URI
|
from pisi.uri import URI
|
||||||
|
|
||||||
|
|
||||||
class Error(pisi.Error):
|
class Error(pisi.Error):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -132,7 +136,7 @@ class Fetcher:
|
|||||||
from httplib import HTTPException
|
from httplib import HTTPException
|
||||||
|
|
||||||
if os.path.exists(archivefile):
|
if os.path.exists(archivefile):
|
||||||
if self.scheme == "http" or self.scheme == "https":
|
if self.scheme == "http" or self.scheme == "https" or self.scheme == "ftp":
|
||||||
self.existsize = os.path.getsize(archivefile)
|
self.existsize = os.path.getsize(archivefile)
|
||||||
dest = open(archivefile, "ab")
|
dest = open(archivefile, "ab")
|
||||||
else:
|
else:
|
||||||
@@ -169,6 +173,11 @@ class Fetcher:
|
|||||||
opener = urllib2.build_opener(range_handler)
|
opener = urllib2.build_opener(range_handler)
|
||||||
urllib2.install_opener(opener)
|
urllib2.install_opener(opener)
|
||||||
request.add_header('Range', 'bytes=%d-' % self.existsize)
|
request.add_header('Range', 'bytes=%d-' % self.existsize)
|
||||||
|
if self.existsize and self.scheme =="ftp":
|
||||||
|
range_handler = FTPRangeHandler()
|
||||||
|
opener = urllib2.build_opener(range_handler)
|
||||||
|
urllib2.install_opener(opener)
|
||||||
|
request.add_header('Range', 'bytes=%d-' % self.existsize)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def err (self, error):
|
def err (self, error):
|
||||||
@@ -182,8 +191,110 @@ class HTTPRangeHandler(urllib2.BaseHandler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def http_error_206(self, request, fp, errcode, msg, headers):
|
def http_error_206(self, request, fp, errcode, msg, headers):
|
||||||
return addinfourl(fp, headers, request.get_full_url())
|
return urllib.addinfourl(fp, headers, request.get_full_url())
|
||||||
|
|
||||||
def http_error_416(self, request, fp, errcode, msg, headers):
|
def http_error_416(self, request, fp, errcode, msg, headers):
|
||||||
# HTTP 1.1's 'Range Not Satisfiable' error..
|
# HTTP 1.1's 'Range Not Satisfiable' error..
|
||||||
raise FetchError(_('Requested range not satisfiable'))
|
raise FetchError(_('Requested range not satisfiable'))
|
||||||
|
|
||||||
|
|
||||||
|
class FTPRangeHandler(urllib2.FTPHandler):
|
||||||
|
"""
|
||||||
|
FTP Range support..
|
||||||
|
"""
|
||||||
|
def ftp_open(self, req):
|
||||||
|
host = req.get_host()
|
||||||
|
host, port = urllib.splitport(host)
|
||||||
|
if port is None:
|
||||||
|
port = ftplib.FTP_PORT
|
||||||
|
|
||||||
|
try:
|
||||||
|
host = socket.gethostbyname(host)
|
||||||
|
except socket.error, msg:
|
||||||
|
raise FetchError(msg)
|
||||||
|
path, attrs = urllib.splitattr(req.get_selector())
|
||||||
|
dirs = path.split('/')
|
||||||
|
dirs = map(urllib.unquote, dirs)
|
||||||
|
dirs, file = dirs[:-1], dirs[-1]
|
||||||
|
if dirs and not dirs[0]:
|
||||||
|
dirs = dirs[1:]
|
||||||
|
try:
|
||||||
|
fw = self.connect_ftp('', '', host, port, dirs)
|
||||||
|
type = file and 'I' or 'D'
|
||||||
|
for attr in attrs:
|
||||||
|
attr, value = urllib.splitattr(attr)
|
||||||
|
if attr.lower() == 'type' and \
|
||||||
|
value in ('a', 'A', 'i', 'I', 'd', 'D'):
|
||||||
|
type = value.upper()
|
||||||
|
|
||||||
|
rawr = req.headers.get('Range', None)
|
||||||
|
rest = int(rawr.split("=")[1].rstrip("-"))
|
||||||
|
|
||||||
|
fp, retrlen = fw.retrfile(file, type, rest)
|
||||||
|
|
||||||
|
fb, lb = rest, retrlen
|
||||||
|
if retrlen is None or retrlen == 0:
|
||||||
|
raise FetchError(_('Requested Range Not Satisfiable'))
|
||||||
|
retrlen = lb - fb
|
||||||
|
if retrlen < 0:
|
||||||
|
# beginning of range is larger than file
|
||||||
|
raise FetchError(_('Requested Range Not Satisfiable'))
|
||||||
|
|
||||||
|
headers = ""
|
||||||
|
mtype = guess_type(req.get_full_url())[0]
|
||||||
|
if mtype:
|
||||||
|
headers += "Content-Type: %s\n" % mtype
|
||||||
|
if retrlen is not None and retrlen >= 0:
|
||||||
|
headers += "Content-Length: %d\n" % retrlen
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError, msg:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
|
||||||
|
except ftplib.all_errors, msg:
|
||||||
|
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
|
||||||
|
|
||||||
|
def connect_ftp(self, user, passwd, host, port, dirs):
|
||||||
|
fw = ftpwrapper('', '', host, port, dirs)
|
||||||
|
return fw
|
||||||
|
|
||||||
|
class ftpwrapper(urllib.ftpwrapper):
|
||||||
|
def retrfile(self, file, type, rest=None):
|
||||||
|
self.endtransfer()
|
||||||
|
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
|
||||||
|
else: cmd = 'TYPE ' + type; isdir = 0
|
||||||
|
try:
|
||||||
|
self.ftp.voidcmd(cmd)
|
||||||
|
except ftplib.all_errors:
|
||||||
|
self.init()
|
||||||
|
self.ftp.voidcmd(cmd)
|
||||||
|
conn = None
|
||||||
|
if file and not isdir:
|
||||||
|
try:
|
||||||
|
self.ftp.nlst(file)
|
||||||
|
except ftplib.error_perm, reason:
|
||||||
|
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||||
|
# Restore the transfer mode!
|
||||||
|
self.ftp.voidcmd(cmd)
|
||||||
|
try:
|
||||||
|
cmd = 'RETR ' + file
|
||||||
|
conn = self.ftp.ntransfercmd(cmd, rest)
|
||||||
|
except ftplib.error_perm, reason:
|
||||||
|
if str(reason)[:3] == '501':
|
||||||
|
# workaround for REST not suported error
|
||||||
|
fp, retrlen = self.retrfile(file, type)
|
||||||
|
fp = RangeableFileObject(fp, (rest,''))
|
||||||
|
return (fp, retrlen)
|
||||||
|
elif str(reason)[:3] != '550':
|
||||||
|
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||||
|
if not conn:
|
||||||
|
self.ftp.voidcmd('TYPE A')
|
||||||
|
if file: cmd = 'LIST ' + file
|
||||||
|
else: cmd = 'LIST'
|
||||||
|
conn = self.ftp.ntransfercmd(cmd)
|
||||||
|
self.busy = 1
|
||||||
|
return (urllib.addclosehook(conn[0].makefile('rb'),
|
||||||
|
self.endtransfer), conn[1])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user