2021-08-29 00:02:47 +08:00

352 lines
13 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python2
# -*- coding:utf-8 -*-
#=========================================
# Filename : transfer.py
# Author : Colben
# Create : 2017-05-11 12:27
#=========================================
import os, sys, platform
import shutil
import re
import time
import zipfile
import BaseHTTPServer, HTMLParser
import requests, mimetypes, base64
from SocketServer import ThreadingMixIn
from threading import Thread
reload(sys)
sys.setdefaultencoding("utf-8")
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
KEY = base64.b64encode('lijc:lijc')
AUTH = ('lijc', 'lijc')
URLS = []
THREAD_RESULT = {}
def sizeof_fmt(num):# <<FOLD<<
for x in ['B', 'KB', 'MB', 'GB']:
if num < 1024.0:
return "%3.1f%s" % (num, x)
num /= 1024.0
return "%3.1f%s" % (num, 'TB')# >>FOLD>>
def modification_date(filename):
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(filename)))
def handle_attachment(filename, user_agent):# <<FOLD<<
r, info = True, "File '%s' upload success!"
if filename.endswith('.zip'):
try:
zf = zipfile.ZipFile(filename, 'r')
zf.extractall('/home/attachment/')
except Exception, e:
r, info = False, str(e)
else:
try:
dest_file = os.path.join('/home/attachment/', os.path.basename(filename))
if os.path.exists(dest_file):
os.remove(dest_file)
os.rename(filename, dest_file)
filename = dest_file
except Exception, e:
r, info = False, str(e)
if r and user_agent != 'upload/sync':
for url in URLS:
sub_thread = Thread(target=sync_file, args=(url, filename,))
sub_thread.setDaemon(1)
sub_thread.start()
return (r, info%os.path.basename(filename))# >>FOLD>>
def sync_file(url, filename):# <<FOLD<<
THREAD_RESULT[url] = '<br>Syncing to %s ...'%url
headers = {'User-Agent': 'upload/sync'}
files = {'file': (os.path.basename(filename), open(filename, 'rb'), 'upload/file')}
resp = requests.post(url, auth=AUTH, headers=headers, files=files)
THREAD_RESULT[url] = "<br>Sync to '%s': %s"%(url, resp.headers['Sync-Result'])
return# >>FOLD>>
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):# <<FOLD<<
parser=HTMLParser.HTMLParser()
#def do_GET(self):# <<FOLD<<
# """Serve a GET request."""
# f = self.send_head()
# if f:
# self.copyfile(f, self.wfile)
# f.close()# >>FOLD>>
def do_GET(self):# <<FOLD<<
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
pass
elif self.headers.getheader('Authorization') == 'Basic '+KEY:
"""Serve a GET request."""
f = self.send_head()
if f:
self.copyfile(f, self.wfile)
f.close()
pass
else:
self.do_AUTHHEAD()
self.wfile.write(self.headers.getheader('Authorization'))
self.wfile.write('not authenticated')
pass# >>FOLD>>
def do_HEAD(self):# <<FOLD<<
"""Serve a HEAD request."""
f = self.send_head()
if f:
f.close()# >>FOLD>>
def do_AUTHHEAD(self):# <<FOLD<<
print "send header"
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"')
self.send_header('Content-type', 'text/html')
self.end_headers()# >>FOLD>>
def do_POST(self):# <<FOLD<<
"""Serve a POST request."""
r, info = self.deal_post_data()
f = StringIO()
f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write('<html>\n<title>Upload Result</title>\n')
f.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n')
f.write('<body>\n<h2>Upload Result</h2>\n')
f.write('\n<br><a href="../">返回上级</a>&nbsp&nbsp<a href="/">返回首页</a>')
f.write('<hr>\n')
if r:
f.write('<strong>Success:</strong>')
else:
f.write('<strong>Failed:</strong>')
f.write(info)
self.send_footer(f)
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(length))
if self.headers['user-agent'] == 'upload/sync':
self.send_header("Sync-Result", info)
self.end_headers()
if f:
self.copyfile(f, self.wfile)
f.close()# >>FOLD>>
def deal_post_data(self):# <<FOLD<<
boundary = self.headers.plisttext.split("=")[1]
remainbytes = int(self.headers['content-length'])
line = self.rfile.readline()
remainbytes -= len(line)
if not boundary in line:
return (False, "Content NOT begin with boundary")
line = self.rfile.readline()
remainbytes -= len(line)
fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line)
if not fn:
return (False, "Can't find out file name...")
path = self.translate_path(self.path)
osType = platform.system()
try:
if osType != "Linux":
fn = os.path.join(path, fn[0].decode('gbk').encode('utf-8'))
else:
fn = os.path.join(path, fn[0])
except Exception, e:
return (False, "文件名请不要用中文或者使用IE上传中文名的文件。")
fn_utf8 = self.parser.unescape(fn)
if os.path.exists(fn_utf8):
os.remove(fn_utf8)
line = self.rfile.readline()
remainbytes -= len(line)
line = self.rfile.readline()
remainbytes -= len(line)
try:
out = open(fn_utf8, 'wb')
except IOError:
return (False, "Can't create file to write, do you have permission to write?")
preline = self.rfile.readline()
remainbytes -= len(preline)
while remainbytes > 0:
line = self.rfile.readline()
remainbytes -= len(line)
if boundary in line:
preline = preline[0:-1]
if preline.endswith('\r'):
preline = preline[0:-1]
out.write(preline)
out.close()
if os.path.dirname(fn).endswith('/attachment'):
return handle_attachment(fn, self.headers['user-agent'])
else:
return (True, "File '%s' upload success!" % os.path.basename(fn))
else:
out.write(preline)
preline = line
return (False, "Unexpect Ends of data.")# >>FOLD>>
def send_head(self):# <<FOLD<<
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
for index in "index.html", "index.htm":
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
# Always read in binary mode. Opening files in text mode may cause
# newline translations, making the actual size of the content
# transmitted *less* than the content-length!
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.end_headers()
return f# >>FOLD>>
def list_directory(self, path):# <<FOLD<<
try:
list = filter(lambda x: not x.startswith('.'), os.listdir(path))
except os.error:
self.send_error(404, "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = StringIO()
displaypath = requests.utils.unquote(self.path)
#displaypath = requests.utils.cgi.escape(requests.utils.unquote(self.path))
f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write('<html>\n<head><title>Directory %s</title>\n' % displaypath)
f.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n')
f.write('<style type="text/css">a:link,a:visited{text-decoration:none;}a:hover{text-decoration:underline;}</style>')
f.write('\n</head>\n<body>\n<h2>当前位置: %s</h2>\n' % displaypath)
if '/' != self.path:
f.write('\n<a href="../">返回上级</a>&nbsp&nbsp<a href="/">返回首页</a>')
f.write('<hr>\n<form ENCTYPE="multipart/form-data" method="post">')
f.write('<input name="file" type="file"/>')
f.write('<input type="submit" value="上传"/>')
f.write('\n</form>\n<hr>\n<table>\n')
if not list:
f.write('空目录\n')
else:
for name in list:
fullname = os.path.join(path, name)
colorName = displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
#colorName = '<span style="color: #0066CC;">' + name + '/</span>'
colorName = name + '/'
displayname = name
linkname = name + "/"
if os.path.isfile(fullname):
colorName = '<span style="color: #9F5000;">' + name + '</span>'
displayname = name
if os.path.islink(fullname):
colorName = '<span style="color: #00CACA;">' + name + '@</span>'
displayname = name
# Note: a link to a directory displays with @ and links with /
filename = os.getcwd() + '/' + displaypath + displayname
f.write(
'<tr><td width="66%%"><a href="%s">%s</a></td><td width="9%%">%s</td><td width="25%%">%s</td></tr>\n'
% (requests.utils.quote(linkname), colorName,
sizeof_fmt(os.path.getsize(filename)), modification_date(filename)))
f.write('</table>')
self.send_footer(f)
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(length))
self.end_headers()
return f# >>FOLD>>
def send_footer(self, f):# <<FOLD<<
f.write('\n<hr><b>同步状态:</b>')
for sync_result in THREAD_RESULT.values():
f.write(sync_result)
f.write('\n<hr>\n<b>注意事项:</b>\n<ul>\n')
f.write('<li>上传同名文件时,会覆盖已存在的旧文件</li>\n')
f.write('</ul>')
f.write('\n<hr>\n<b>站外链接:</b>\n<ul>\n')
f.write('<li><a href="https://www.baidu.com/">百度</a></li>\n')
f.write('</ul><hr>\n</body>\n</html>\n')# >>FOLD>>
def translate_path(self, path):# <<FOLD<<
# abandon query parameters
path = path.split('?', 1)[0]
path = path.split('#', 1)[0]
path = os.path.normpath(requests.utils.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.getcwd()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path# >>FOLD>>
def copyfile(self, source, outputfile):
shutil.copyfileobj(source, outputfile)
def guess_type(self, path):# <<FOLD<<
base, ext = os.path.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return self.extensions_map['']# >>FOLD>>
if not mimetypes.inited:
mimetypes.init() # try to read system mime.types
extensions_map = mimetypes.types_map.copy()
extensions_map.update({
'': 'application/octet-stream', # Default
'.py': 'text/plain',
'.c': 'text/plain',
'.h': 'text/plain',
})# >>FOLD>>
class ThreadingServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
if __name__ == '__main__':
# test()
# 单线程
# srvr = BaseHTTPServer.HTTPServer(('', 12306), SimpleHTTPRequestHandler)
# 多线程
srvr = ThreadingServer(('', 12306), SimpleHTTPRequestHandler)
print 'Listening ', srvr.server_address ,'...'
srvr.serve_forever()