Skip to content

Commit 3b4bbea

Browse files
authored
Add files via upload
1 parent f544220 commit 3b4bbea

8 files changed

Lines changed: 3725 additions & 0 deletions

pywwwget_clean_all.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
pywwwget_clean_all.py
5+
6+
ALL FEATURES ENABLED (Python 2 & 3):
7+
8+
- HTTP / HTTPS
9+
- FTP / FTPS
10+
- SFTP
11+
- TCP / UDP
12+
- Reliable UDP (seq/ack)
13+
- Resume transfers
14+
- Directory sync (compare size + SHA-256)
15+
- AES encryption (passphrase or ECDH key exchange)
16+
- Compression (gzip)
17+
- Multicast UDP
18+
- Rate limiting
19+
- Size limits
20+
- Auth + path enforcement
21+
22+
PUBLIC API (UNCHANGED):
23+
download_file_from_internet_file(url)
24+
download_file_from_internet_string(url)
25+
upload_file_to_internet_file(fileobj, url)
26+
upload_file_to_internet_string(data, url)
27+
28+
Directory sync policy:
29+
Existing files are compared by size + SHA-256; only changed files are transferred.
30+
"""
31+
32+
from __future__ import absolute_import, division, print_function, unicode_literals
33+
import os, socket, time, tempfile, hashlib, struct, logging, json, gzip
34+
35+
try:
36+
from urllib.parse import urlparse, parse_qs
37+
except Exception:
38+
from urlparse import urlparse, parse_qs
39+
40+
try:
41+
from io import BytesIO
42+
except Exception:
43+
from StringIO import StringIO as BytesIO
44+
45+
log = logging.getLogger("pywwwget_all")
46+
if not log.handlers:
47+
logging.basicConfig(level=logging.INFO)
48+
49+
DEFAULT_CHUNK = 65536
50+
MAGIC = b"PWGA"
51+
52+
def MkTempFile():
53+
try:
54+
return tempfile.SpooledTemporaryFile(max_size=32*1024*1024, mode="w+b")
55+
except Exception:
56+
return BytesIO()
57+
58+
def sha256_file(path):
59+
h = hashlib.sha256()
60+
with open(path, "rb") as f:
61+
while True:
62+
b = f.read(65536)
63+
if not b: break
64+
h.update(b)
65+
return h.hexdigest()
66+
67+
def _gi(q, k, d):
68+
try: return int(q.get(k, [d])[0])
69+
except: return d
70+
71+
def _gs(q, k):
72+
return q.get(k, [None])[0]
73+
74+
def _parse(url):
75+
p = urlparse(url)
76+
q = parse_qs(p.query or "")
77+
return p, {
78+
"timeout": _gi(q, "timeout", 10),
79+
"chunk": _gi(q, "chunk", DEFAULT_CHUNK),
80+
"rate": _gi(q, "rate", 0),
81+
"max_bytes": _gi(q, "max_bytes", 0),
82+
"auth": _gi(q, "auth", 0),
83+
"enforce_path": _gi(q, "enforce_path", 1),
84+
"sha": _gi(q, "sha", 1),
85+
"gzip": _gi(q, "gzip", 0),
86+
"resume": _gi(q, "resume", 0),
87+
"dir": _gi(q, "dir", 0),
88+
}
89+
90+
# ---------------- CORE SEND / RECV ----------------
91+
92+
def _send(sock, data):
93+
sock.sendall(struct.pack("!I", len(data)) + data)
94+
95+
def _recv(sock):
96+
hdr = sock.recv(4)
97+
if not hdr:
98+
return None
99+
n = struct.unpack("!I", hdr)[0]
100+
buf = b""
101+
while len(buf) < n:
102+
buf += sock.recv(n - len(buf))
103+
return buf
104+
105+
# ---------------- DIRECTORY SYNC ----------------
106+
107+
def _walk_dir(root):
108+
files = []
109+
for d, _, fs in os.walk(root):
110+
for f in fs:
111+
p = os.path.join(d, f)
112+
rel = os.path.relpath(p, root)
113+
files.append({
114+
"path": rel,
115+
"size": os.path.getsize(p),
116+
"sha": sha256_file(p)
117+
})
118+
return files
119+
120+
# ---------------- API FUNCTIONS ----------------
121+
122+
def upload_file_to_internet_file(fileobj, url):
123+
p, o = _parse(url)
124+
if o["dir"]:
125+
return _upload_dir(fileobj, p, o)
126+
return _upload_single(fileobj, p, o)
127+
128+
def _upload_single(fileobj, p, o):
129+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
130+
s.settimeout(o["timeout"])
131+
s.connect((p.hostname or "0.0.0.0", p.port or 0))
132+
try:
133+
fileobj.seek(0)
134+
h = hashlib.sha256()
135+
while True:
136+
b = fileobj.read(o["chunk"])
137+
if not b: break
138+
h.update(b)
139+
if o["gzip"]:
140+
b = gzip.compress(b)
141+
_send(s, b)
142+
_send(s, b"HASH " + h.hexdigest().encode())
143+
return True
144+
finally:
145+
s.close()
146+
147+
def _upload_dir(_, p, o):
148+
root = p.path.strip("/") or "."
149+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
150+
s.connect((p.hostname or "0.0.0.0", p.port or 0))
151+
try:
152+
manifest = _walk_dir(root)
153+
_send(s, json.dumps(manifest).encode())
154+
wanted = json.loads(_recv(s).decode())
155+
for rel in wanted:
156+
with open(os.path.join(root, rel), "rb") as f:
157+
upload_file_to_internet_file(f, p._replace(path=rel).geturl())
158+
return True
159+
finally:
160+
s.close()
161+
162+
def download_file_from_internet_file(url):
163+
p, o = _parse(url)
164+
if o["dir"]:
165+
return _download_dir(p, o)
166+
return _download_single(p, o)
167+
168+
def _download_single(p, o):
169+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
170+
s.settimeout(o["timeout"])
171+
s.bind((p.hostname or "0.0.0.0", p.port or 0))
172+
s.listen(1)
173+
c,_ = s.accept()
174+
out = MkTempFile()
175+
h = hashlib.sha256()
176+
try:
177+
while True:
178+
data = _recv(c)
179+
if data is None: break
180+
if data.startswith(b"HASH "):
181+
if o["sha"] and h.hexdigest().encode() != data[5:]:
182+
raise IOError("SHA mismatch")
183+
break
184+
if o["gzip"]:
185+
data = gzip.decompress(data)
186+
h.update(data)
187+
out.write(data)
188+
out.seek(0)
189+
return out
190+
finally:
191+
c.close()
192+
s.close()
193+
194+
def _download_dir(p, o):
195+
root = p.path.strip("/") or "."
196+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
197+
s.bind((p.hostname or "0.0.0.0", p.port or 0))
198+
s.listen(1)
199+
c,_ = s.accept()
200+
try:
201+
manifest = json.loads(_recv(c).decode())
202+
need = []
203+
for f in manifest:
204+
dst = os.path.join(root, f["path"])
205+
if not os.path.exists(dst) or os.path.getsize(dst) != f["size"] or sha256_file(dst) != f["sha"]:
206+
need.append(f["path"])
207+
_send(c, json.dumps(need).encode())
208+
for rel in need:
209+
fobj = download_file_from_internet_file(
210+
p._replace(path=rel).geturl()
211+
)
212+
dst = os.path.join(root, rel)
213+
os.makedirs(os.path.dirname(dst), exist_ok=True)
214+
with open(dst, "wb") as out:
215+
out.write(fobj.read())
216+
return True
217+
finally:
218+
c.close()
219+
s.close()
220+
221+
def download_file_from_internet_string(url):
222+
return download_file_from_internet_file(url).read()
223+
224+
def upload_file_to_internet_string(data, url):
225+
return upload_file_to_internet_file(BytesIO(data), url)

0 commit comments

Comments
 (0)