Skip to content

Commit 0494018

Browse files
authored
Add files via upload
1 parent 008d77d commit 0494018

8 files changed

Lines changed: 2838 additions & 44 deletions

PyWWW/pywwwget_chatgpt.py

Lines changed: 355 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,355 @@ def _copy_fileobj_to_path(fileobj, path, overwrite=False):
944944
pass
945945
shutil.copyfileobj(fileobj, out)
946946

947+
# TFTP opcodes
948+
OP_RRQ = 1
949+
OP_WRQ = 2
950+
OP_DATA = 3
951+
OP_ACK = 4
952+
OP_ERROR = 5
953+
954+
BLOCK_SIZE = 512
955+
956+
957+
class TFTPError(Exception):
958+
pass
959+
960+
961+
def _make_rrq(filename, mode=b"octet"):
962+
# RRQ: 2 bytes opcode, filename, 0, mode, 0
963+
return struct.pack("!H", OP_RRQ) + _to_bytes(filename) + b"\x00" + _to_bytes(mode) + b"\x00"
964+
965+
966+
def _make_wrq(filename, mode=b"octet"):
967+
return struct.pack("!H", OP_WRQ) + _to_bytes(filename) + b"\x00" + _to_bytes(mode) + b"\x00"
968+
969+
970+
def _make_data(blockno, payload):
971+
return struct.pack("!HH", OP_DATA, blockno) + payload
972+
973+
974+
def _make_ack(blockno):
975+
return struct.pack("!HH", OP_ACK, blockno)
976+
977+
978+
def _parse_packet(pkt):
979+
if len(pkt) < 2:
980+
raise TFTPError("Short packet")
981+
op = struct.unpack("!H", pkt[:2])[0]
982+
return op
983+
984+
985+
def _parse_ack(pkt):
986+
if len(pkt) < 4:
987+
raise TFTPError("Short ACK")
988+
op, blockno = struct.unpack("!HH", pkt[:4])
989+
if op != OP_ACK:
990+
raise TFTPError("Expected ACK, got opcode %d" % op)
991+
return blockno
992+
993+
994+
def _parse_data(pkt):
995+
if len(pkt) < 4:
996+
raise TFTPError("Short DATA")
997+
op, blockno = struct.unpack("!HH", pkt[:4])
998+
if op != OP_DATA:
999+
raise TFTPError("Expected DATA, got opcode %d" % op)
1000+
return blockno, pkt[4:]
1001+
1002+
1003+
def _parse_error(pkt):
1004+
# ERROR: opcode(2) + errcode(2) + errmsg + 0
1005+
if len(pkt) < 4:
1006+
raise TFTPError("Short ERROR")
1007+
op, errcode = struct.unpack("!HH", pkt[:4])
1008+
if op != OP_ERROR:
1009+
raise TFTPError("Not an ERROR packet")
1010+
msg = pkt[4:]
1011+
if b"\x00" in msg:
1012+
msg = msg.split(b"\x00", 1)[0]
1013+
try:
1014+
msg = msg.decode("utf-8", "replace")
1015+
except Exception:
1016+
msg = repr(msg)
1017+
raise TFTPError("TFTP ERROR %d: %s" % (errcode, msg))
1018+
1019+
1020+
def _mk_sock(proxy, timeout):
1021+
"""
1022+
proxy: dict or None
1023+
If dict, expected keys:
1024+
host, port, username(optional), password(optional)
1025+
"""
1026+
s = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM)
1027+
s.settimeout(timeout)
1028+
1029+
if proxy:
1030+
# Only SOCKS5 is realistic for UDP.
1031+
s.set_proxy(
1032+
proxy_type=socks.SOCKS5,
1033+
addr=proxy["host"],
1034+
port=int(proxy["port"]),
1035+
username=proxy.get("username"),
1036+
password=proxy.get("password"),
1037+
rdns=True,
1038+
)
1039+
return s
1040+
1041+
1042+
def tftp_upload(server_host, remote_filename, fileobj,
1043+
server_port=69, mode="octet",
1044+
proxy=None, timeout=5.0, retries=5):
1045+
"""
1046+
Upload to a TFTP server using a file object opened for reading (binary).
1047+
1048+
Args:
1049+
server_host (str): TFTP server hostname/IP
1050+
remote_filename (str): destination filename on server
1051+
fileobj: readable file-like object (must return bytes)
1052+
proxy (dict|None): {"host": "...", "port": 1080, "username": "...", "password": "..."}
1053+
timeout (float): socket timeout seconds
1054+
retries (int): retransmit attempts per block
1055+
1056+
Returns:
1057+
None (raises TFTPError on failure)
1058+
"""
1059+
sock = _mk_sock(proxy, timeout)
1060+
1061+
try:
1062+
# Send WRQ to well-known port
1063+
wrq = _make_wrq(remote_filename, mode=_to_bytes(mode))
1064+
sock.sendto(wrq, (server_host, int(server_port)))
1065+
1066+
# Server should respond from a new ephemeral port with ACK(0)
1067+
for attempt in range(retries):
1068+
try:
1069+
pkt, addr = sock.recvfrom(4 + 128)
1070+
op = _parse_packet(pkt)
1071+
if op == OP_ERROR:
1072+
_parse_error(pkt)
1073+
if op != OP_ACK:
1074+
raise TFTPError("Expected ACK(0), got opcode %d" % op)
1075+
ack_block = _parse_ack(pkt)
1076+
if ack_block != 0:
1077+
raise TFTPError("Expected ACK block 0, got %d" % ack_block)
1078+
server_tid = addr # (ip, port) for rest of transfer
1079+
break
1080+
except socket.timeout:
1081+
# Retransmit WRQ
1082+
sock.sendto(wrq, (server_host, int(server_port)))
1083+
else:
1084+
raise TFTPError("Timeout waiting for ACK(0)")
1085+
1086+
# Send DATA blocks starting at 1
1087+
blockno = 1
1088+
while True:
1089+
data = fileobj.read(BLOCK_SIZE)
1090+
if data is None:
1091+
data = b""
1092+
if not isinstance(data, (bytes, bytearray)):
1093+
raise TFTPError("fileobj.read() must return bytes")
1094+
1095+
data_pkt = _make_data(blockno, data)
1096+
1097+
# retransmit loop for this block
1098+
for attempt in range(retries):
1099+
sock.sendto(data_pkt, server_tid)
1100+
try:
1101+
pkt, addr = sock.recvfrom(4 + 128)
1102+
# TID check: ignore packets from other ports/hosts
1103+
if addr != server_tid:
1104+
continue
1105+
op = _parse_packet(pkt)
1106+
if op == OP_ERROR:
1107+
_parse_error(pkt)
1108+
ackb = _parse_ack(pkt)
1109+
if ackb == blockno:
1110+
break
1111+
except socket.timeout:
1112+
continue
1113+
else:
1114+
raise TFTPError("Timeout waiting for ACK(%d)" % blockno)
1115+
1116+
# Last block is < 512 bytes (including 0 bytes if exact multiple requires final 0-length block)
1117+
if len(data) < BLOCK_SIZE:
1118+
return
1119+
1120+
blockno = (blockno + 1) & 0xFFFF
1121+
if blockno == 0:
1122+
# TFTP block rolls over after 65535; handling wrap robustly is more involved.
1123+
raise TFTPError("Block number rollover not supported in this simple implementation.")
1124+
1125+
finally:
1126+
try:
1127+
sock.close()
1128+
except Exception:
1129+
pass
1130+
1131+
1132+
def tftp_download(server_host, remote_filename,
1133+
server_port=69, mode="octet",
1134+
proxy=None, timeout=5.0, retries=5):
1135+
"""
1136+
Download from a TFTP server and return a file-like object containing bytes.
1137+
1138+
Args:
1139+
server_host (str): TFTP server hostname/IP
1140+
remote_filename (str): filename on server
1141+
proxy (dict|None): {"host": "...", "port": 1080, "username": "...", "password": "..."}
1142+
timeout (float): socket timeout seconds
1143+
retries (int): retransmit attempts
1144+
1145+
Returns:
1146+
io.BytesIO: file-like object positioned at start
1147+
"""
1148+
sock = _mk_sock(proxy, timeout)
1149+
out = MkTempFile()
1150+
1151+
rrq = _make_rrq(remote_filename, mode=_to_bytes(mode))
1152+
1153+
try:
1154+
# Send RRQ to well-known port
1155+
sock.sendto(rrq, (server_host, int(server_port)))
1156+
1157+
expected = 1
1158+
server_tid = None
1159+
last_ack = 0
1160+
1161+
while True:
1162+
for attempt in range(retries):
1163+
try:
1164+
pkt, addr = sock.recvfrom(4 + BLOCK_SIZE + 128)
1165+
op = _parse_packet(pkt)
1166+
1167+
if op == OP_ERROR:
1168+
_parse_error(pkt)
1169+
1170+
if op != OP_DATA:
1171+
raise TFTPError("Expected DATA, got opcode %d" % op)
1172+
1173+
blockno, payload = _parse_data(pkt)
1174+
1175+
# First DATA defines server TID
1176+
if server_tid is None:
1177+
server_tid = addr
1178+
1179+
# Ignore packets from unexpected TID
1180+
if addr != server_tid:
1181+
continue
1182+
1183+
if blockno == expected:
1184+
out.write(payload)
1185+
ack = _make_ack(blockno)
1186+
sock.sendto(ack, server_tid)
1187+
last_ack = blockno
1188+
1189+
# end condition
1190+
if len(payload) < BLOCK_SIZE:
1191+
out.seek(0)
1192+
return out
1193+
1194+
expected = (expected + 1) & 0xFFFF
1195+
if expected == 0:
1196+
raise TFTPError("Block number rollover not supported in this simple implementation.")
1197+
break
1198+
1199+
elif blockno == last_ack:
1200+
# Duplicate DATA; re-ACK to help server
1201+
sock.sendto(_make_ack(blockno), server_tid)
1202+
break
1203+
1204+
else:
1205+
# Out-of-order: ACK last good block
1206+
sock.sendto(_make_ack(last_ack), server_tid)
1207+
break
1208+
1209+
except socket.timeout:
1210+
# On timeout, retransmit RRQ initially, else retransmit last ACK
1211+
if server_tid is None:
1212+
sock.sendto(rrq, (server_host, int(server_port)))
1213+
else:
1214+
sock.sendto(_make_ack(last_ack), server_tid)
1215+
else:
1216+
raise TFTPError("Timeout receiving DATA block %d" % expected)
1217+
1218+
finally:
1219+
try:
1220+
sock.close()
1221+
except Exception:
1222+
pass
1223+
1224+
def download_file_from_tftp_file(url, resumefile=None, timeout=60, returnstats=False):
1225+
p = urlparse(url)
1226+
if p.scheme != "tftp":
1227+
return False
1228+
1229+
host = p.hostname
1230+
port = p.port or 69
1231+
user = p.username
1232+
pw = p.password
1233+
path = p.path or "/"
1234+
file_dir = os.path.dirname(path)
1235+
start_time = time.time()
1236+
socket.setdefaulttimeout(float(timeout))
1237+
try:
1238+
bio = tftp_download(host, p.path, port, timeout=float(timeout))
1239+
fulldatasize = bio.tell()
1240+
bio.seek(0, 0)
1241+
end_time = time.time()
1242+
total_time = end_time - start_time
1243+
if(returnstats):
1244+
returnval = {'Type': "Buffer", 'Buffer': bio, 'Contentsize': fulldatasize, 'ContentsizeAlt': {'IEC': get_readable_size(fulldatasize, 2, "IEC"), 'SI': get_readable_size(fulldatasize, 2, "SI")}, 'Headers': None, 'Version': None, 'Method': None, 'HeadersSent': None, 'URL': url, 'Code': None, 'RequestTime': {'StartTime': start_time, 'EndTime': end_time, 'TotalTime': total_time}, 'FTPLib': 'pyftp'}
1245+
else:
1246+
return bio
1247+
except Exception:
1248+
try:
1249+
ftp.close()
1250+
except Exception:
1251+
pass
1252+
return False
1253+
1254+
def download_file_from_tftp_string(url, resumefile=None, timeout=60, returnstats=False):
1255+
fp = download_file_from_tftp_file(url, resumefile, timeout, returnstats)
1256+
return fp.read() if fp else False
1257+
1258+
def upload_file_to_tftp_file(fileobj, url, timeout=60):
1259+
p = urlparse(url)
1260+
if p.scheme != "tftp":
1261+
return False
1262+
1263+
socket.setdefaulttimeout(float(timeout))
1264+
host = p.hostname
1265+
port = p.port or 21
1266+
user = p.username
1267+
pw = p.password
1268+
path = p.path or "/"
1269+
file_dir = os.path.dirname(path)
1270+
fname = os.path.basename(path) or "upload.bin"
1271+
1272+
try:
1273+
try:
1274+
fileobj.seek(0, 0)
1275+
except Exception:
1276+
pass
1277+
tftp_upload(host, p.path, fileobj, port, timeout=float(timeout))
1278+
try:
1279+
fileobj.seek(0, 0)
1280+
except Exception:
1281+
pass
1282+
1283+
return fileobj
1284+
except Exception:
1285+
return False
1286+
1287+
def upload_file_to_tftp_string(data, url, timeout=60):
1288+
bio = MkTempFile(_to_bytes(data))
1289+
out = upload_file_to_tftp_file(bio, url, timeout)
1290+
try:
1291+
bio.close()
1292+
except Exception:
1293+
pass
1294+
return out
1295+
9471296
# --------------------------
9481297
# FTP helpers
9491298
# --------------------------
@@ -5114,6 +5463,8 @@ def download_file_from_internet_file(url, **kwargs):
51145463
return download_file_from_http_file(url, **kwargs)
51155464
if p.scheme in ("ftp", "ftps"):
51165465
return download_file_from_ftp_file(url, **kwargs)
5466+
if p.scheme in ("tftp", ):
5467+
return download_file_from_tftp_file(url, **kwargs)
51175468
if p.scheme in ("sftp", "scp"):
51185469
if __use_pysftp__ and havepysftp:
51195470
return download_file_from_pysftp_file(url, **kwargs)
@@ -5910,10 +6261,14 @@ def upload_file_to_internet_file(fileobj, url):
59106261
return _serve_file_over_http(fileobj, url)
59116262
if p.scheme in ("ftp", "ftps"):
59126263
return upload_file_to_ftp_file(fileobj, url)
6264+
if p.scheme in ("tftp", ):
6265+
return upload_file_to_tftp_file(fileobj, url)
59136266
if p.scheme in ("sftp", "scp"):
59146267
if __use_pysftp__ and havepysftp:
59156268
return upload_file_to_pysftp_file(fileobj, url)
59166269
return upload_file_to_sftp_file(fileobj, url)
6270+
if p.scheme in ("data", ):
6271+
return data_url_encode(fileobj)
59176272
if p.scheme in ("file" or ""):
59186273
outfile = io.open(unquote(p.path), "wb")
59196274
try:
@@ -5923,8 +6278,6 @@ def upload_file_to_internet_file(fileobj, url):
59236278
with io.open(unquote(p.path), "wb") as fdst:
59246279
shutil.copyfileobj(fileobj, fdst)
59256280
return fileobj
5926-
if p.scheme in ("data", ):
5927-
return data_url_encode(fileobj)
59286281
if p.scheme in ("tcp", "udp"):
59296282
parts, o = _parse_net_url(url)
59306283
host = parts.hostname

0 commit comments

Comments
 (0)