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

245 lines
7.5 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
import cgi
12 years ago
import BaseHTTPServer
import commands
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
12 years ago
class FileHandler(BaseHTTPServer.BaseHTTPRequestHandler):
fileName = "Undefined"
filePath = "/dev/null"
fileLength = 0
blockSize = 1024 * 1024
def do_GET(self):
if urllib.unquote(self.path) != "/" + self.fileName:
self.send_response(302)
self.send_header('Location', '/' + self.fileName)
self.end_headers()
return
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')
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 FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
targetDir = "unknown"
uploadPage = """
<!docype html>
<html>
<form action="/" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Upload" />
</form>
</html>
"""
def do_GET(self):
self.sendResponse(200, self.uploadPage)
def do_POST(self):
env = os.environ
env['REQUEST_METHOD'] = "POST"
ctype = self.headers.getheader('content-type')
fstorage = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=env)
if not (ctype and ctype.lower().startswith("multipart/form-data")):
self.sendResponse(400, "Request was not a multipart request. If you want to upload data with curl, do a PUT request, e.g. curl -X PUT -d @file http://ip:port/your-filename")
if not "file" in fstorage:
self.sendResponse(400, "No file found in request.")
return
print dir(fstorage["file"])
print fstorage["file"].filename,
cleanFileName = self.cleanFileName(fstorage["file"].filename)
if cleanFileName == "":
self.sendResponse(400, "Filename was empty")
return
destFileName = self.targetDir + "/" + cleanFileName
if os.path.exists(destFileName):
i = 1
extraDestFileName = destFileName + "(%s)" % i
while os.path.exists(extraDestFileName):
i += 1
extraDestFileName = destFileName + "(%s)" % i
destFileName = extraDestFileName
print fstorage["file"].file
target = open(destFileName, "w")
target.write(fstorage["file"].file.read())
target.close()
msg = "OK!"
self.sendResponse(200, "OK!")
def sendResponse(self, code, msg):
self.send_response(code)
self.send_header('Content-Type', 'text/html')
self.send_header('content-Length', str(len(msg)))
self.end_headers()
self.wfile.write(msg)
def cleanFileName(self, fname):
return fname.replace("/", "")
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
12 years ago
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')
parser.add_argument('-u', '--upload', action="store_true", default=False, \
help="Enable uploads to a given directory")
12 years ago
args = parser.parse_args()
handler = None
dirCreated = False
print args.upload
if not args.upload:
try:
testit = open(args.filename, 'r')
testit.close()
FileHandler.filePath = args.filename
FileHandler.fileName = os.path.basename(args.filename)
FileHandler.fileLength = os.stat(args.filename)[ST_SIZE]
except IOError:
print "Error: Could not open file!"
sys.exit(3)
handler = FileHandler
else:
if os.path.isdir(args.filename):
print "Warning: Uploading to an already existing directory"
elif not os.path.exists(args.filename):
dirCreated = True
try:
os.mkdir(args.filename)
except IOError, OSError:
print "Error: Could not create directory '%s' for uploads" % (args.filename,)
sys.exit(3)
else:
print "Error: Upload directory already exists and is a file"
sys.exit(3)
FilePutter.targetDir = args.filename
handler = FilePutter
server = ThreadedHTTPServer(('', args.port), handler)
if args.upload:
print "Serving \"%s\" under port %d" % (args.filename, args.port)
else:
print "Serving \"%s\" for uploads under port %d" % (args.filename, args.port)
12 years ago
# print urls with local network adresses
print "\nSome addresses this 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()
# cleanup potential upload directory
if dirCreated and len(os.listdir(args.filename)) == 0:
# created upload dir was not used
os.rmdir(args.filename)
12 years ago
print "Good bye.."
if __name__ == '__main__':
main()