You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
servefile/servefile

176 lines
5.3 KiB

12 years ago
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Licensed under GNU General Public License v3 or later
# Written by Sebastian Lohff (seba@seba-geek.de)
# http://seba-geek.de/stuff/servefile/
12 years ago
__version__ = '0.3.2'
12 years ago
import argparse
12 years ago
import BaseHTTPServer
import commands
import datetime
12 years ago
import urllib
import os
12 years ago
import SocketServer
import socket
from stat import ST_SIZE
from subprocess import Popen, PIPE
import sys
import time
def getDateStrNow():
""" Get the current time formatted for HTTP header """
now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
return now.strftime("%a, %d %b %Y %H:%M:%S GMT")
12 years ago
class FileHandler(BaseHTTPServer.BaseHTTPRequestHandler):
fileName = "Undefined"
filePath = "/dev/null"
fileLength = 0
startTime = getDateStrNow()
12 years ago
blockSize = 1024 * 1024
def checkAndDoRedirect(self):
""" If request didn't request self.fileName redirect to self.fileName.
Returns True if a redirect was issued. """
12 years ago
if urllib.unquote(self.path) != "/" + self.fileName:
self.send_response(302)
self.send_header('Location', '/' + self.fileName)
self.end_headers()
return True
return False
def do_HEAD(self):
if self.checkAndDoRedirect():
12 years ago
return
self.send_response(200)
self.send_header('Content-Length', self.fileLength)
self.send_header('Last-Modified', self.startTime)
self.send_header('Content-Type', 'application/octet-stream')
self.end_headers()
12 years ago
def do_GET(self):
12 years ago
myfile = open(self.filePath, 'rb')
# find out if this is a continuing download
fromto = None
12 years ago
if "Range" in self.headers:
cont = self.headers.get("Range").split("=")
12 years ago
if len(cont) > 1 and cont[0] == 'bytes':
fromto = cont[1].split('-')
if len(fromto) > 1:
if fromto[1] == '':
fromto[1] = self.fileLength-1
fromto[0] = int(fromto[0])
fromto[1] = int(fromto[1])
if fromto[0] >= self.fileLength or fromto[0] < 0 or fromto[1] >= self.fileLength or fromto[1]-fromto[0] < 0:
# oops, already done!
self.send_response(416)
self.send_header('Content-Range', 'bytes */%s' % self.fileLength)
self.end_headers()
return
# now we can wind the file *brrrrrr*
myfile.seek(fromto[0])
if fromto != None:
self.send_response(216)
self.send_header('Content-Range', 'bytes %s-%s/%s' % (fromto[0], fromto[1], self.fileLength))
self.send_header('Content-Length', fromto[1]-fromto[0]+1)
else:
self.send_response(200)
self.send_header('Content-Length', self.fileLength)
self.send_header('Content-Disposition', 'attachment; filename="%s"' % self.fileName)
self.send_header('Content-Type', 'application/octet-stream')
12 years ago
self.send_header('Content-Transfer-Encoding', ' binary')
self.end_headers()
block = self.getChunk(myfile, fromto)
while block:
try:
self.wfile.write(block)
except socket.error, e:
print "%s ABORTED transmission (Reason %s: %s)" % (self.client_address[0], e[0], e[1])
return
block = self.getChunk(myfile, fromto)
myfile.close()
print "%s finished downloading" % (self.client_address[0])
return
def getChunk(self, myfile, fromto):
12 years ago
if fromto and myfile.tell()+self.blockSize >= fromto[1]:
12 years ago
readsize = fromto[1]-myfile.tell()+1
else:
readsize = self.blockSize
return myfile.read(readsize)
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
def main():
parser = argparse.ArgumentParser(description='Serve a single file via HTTP')
12 years ago
parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
parser.add_argument('filename', metavar='file/directory', type=str)
parser.add_argument('-p', '--port', type=int, default=8080, \
help='port to listen on')
12 years ago
args = parser.parse_args()
12 years ago
try:
testit = open(args.filename, 'r')
12 years ago
testit.close()
FileHandler.filePath = args.filename
FileHandler.fileName = os.path.basename(args.filename)
FileHandler.fileLength = os.stat(args.filename)[ST_SIZE]
12 years ago
except IOError:
print "Error: Could not open file!"
sys.exit(3)
server = ThreadedHTTPServer(('', args.port), FileHandler)
print "Serving \"%s\" under port %d" % (args.filename, args.port)
12 years ago
# print urls with local network adresses
print "\nSome addresses this file will be available under:"
# ip and ifconfig sometimes are located in /sbin/
os.environ['PATH'] += ':/sbin:/usr/sbin'
proc = Popen(r"ip addr|" + \
"sed -n -e 's/.*inet6\? \([0-9.a-fA-F:]\+\)\/.*/\\1/ p'|" + \
"grep -v '^fe80\|^127.0.0.1\|^::1'", shell=True, stdout=PIPE)
if proc.wait() != 0:
# ip failed somehow, falling back to ifconfig
oldLang = os.environ.get("LC_ALL", None)
os.environ['LC_ALL'] = "C"
proc = Popen(r"ifconfig|" + \
"sed -n 's/.*inet6\? addr: \?\([0-9a-fA-F.:]*\).*/" + \
"\\1/p'|" + \
"grep -v '^fe80\|^127.0.0.1\|^::1'", \
shell=True, stdout=PIPE, stderr=PIPE)
if oldLang:
os.environ['LC_ALL'] = oldLang
else:
del(os.environ['LC_ALL'])
if proc.wait() != 0:
print "Error: Could not locate any ips for you."
proc = None
if proc:
ips = proc.stdout.read().strip()
for ip in ips.split("\n"):
if ip.find(":") >= 0:
# ipv6
ip = "[%s]" % ip
# FIXME: When BaseHTTP supports ipv6 properly, delete this line
continue
print "http://%s:%d/" % (ip, args.port)
12 years ago
try:
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
print "Good bye.."
if __name__ == '__main__':
main()