-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtometa.py
145 lines (128 loc) · 4.27 KB
/
tometa.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# -*- coding: utf-8 -*-
from urllib.parse import parse_qsl
from xml.sax.saxutils import escape, quoteattr
import re
__version__ = "0.5"
def wrap_file(environ, filelike, block_size=8192):
# copied from
# http://legacy.python.org/dev/peps/pep-3333/#optional-platform-specific-file-handling
if 'wsgi.file_wrapper' in environ:
return environ['wsgi.file_wrapper'](filelike, block_size)
else:
return iter(lambda: filelike.read(block_size), '')
class App:
url_re = re.compile("url(?::(.+))?")
metaurl_re = re.compile("metaurl:(.+)")
hash_re = re.compile("hash:(.+)")
def __init__(self, form_path="form.html", error_path="error.html"):
self.form_path = form_path
self.error_path = error_path
def __call__(self, environ, start_response):
qsl = parse_qsl(environ["QUERY_STRING"])
if not qsl:
status = b'200 OK' # HTTP Status
headers = [(b'Content-type', b'text/html; charset=utf-8')]
out = wrap_file(environ, open(self.form_path, "rb"))
else:
try:
out = [self.makelink(qsl).encode("utf8")]
status = b'200 OK' # HTTP Status
# HTTP Headers
headers = [(b'Content-type', b'application/metalink4+xml')]
except Exception:
out = wrap_file(environ, open(self.error_path, "rb"))
status = b"400 Bad Request"
headers = [(b'Content-type', b'text/html; charset=utf-8')]
start_response(status, headers)
return out
@staticmethod
def first_qs(qsl: list, key: str):
"""
return the first parameter value for key
"""
for k, v in qsl:
if k == key:
return v
return None
@classmethod
def getname(cls, qsl: list) -> str:
"""
return the quoted name
"""
name = cls.first_qs(qsl, "name")
if name is None:
raise ValueError("'name' not found in qst")
return quoteattr(name)
@classmethod
def getsize(cls, qsl: list) -> str:
"""
return a size element string
"""
size = cls.first_qs(qsl, "size")
if size:
return "<size>%i</size>" % int(size)
return ""
@classmethod
def geturls(cls, qsl: list) -> str:
"""
return url element strings
"""
outl = []
for k, s in qsl:
m = cls.url_re.match(k)
if m is not None:
if m.group(1):
outl.append("<url location=%s>%s</url>" %
(quoteattr(m.group(1)), escape(s)))
else:
outl.append("<url>%s</url>" % escape(s))
return "\n".join(outl)
@classmethod
def getmetaurls(cls, qsl: list) -> str:
"""
return metaurl elements string
"""
outl = []
for k, s in qsl:
m = cls.metaurl_re.match(k)
if m is not None:
outl.append('<metaurl mediatype=%s>%s</metaurl>' %
(quoteattr(m.group(1)), escape(s)))
return "\n".join(outl)
@classmethod
def gethashes(cls, qsl: list):
"""
return hash elements string
"""
outl = []
for k, s in qsl:
m = cls.hash_re.match(k)
if m is not None:
outl.append('<hash type=%s>%s</hash>' %
(quoteattr(m.group(1)), escape(s)))
return "\n".join(outl)
@classmethod
def makelink(cls, qsl: list) -> str:
"""
return an actual metalink4 xml string
"""
return """<?xml version="1.0" encoding="UTF-8"?>
<metalink xmlns="urn:ietf:params:xml:ns:metalink">
<generator>ToMeta/{version}</generator>
<file name={name}>
{size}
{urls}
{metaurls}
{hashes}
</file>
</metalink>""".format(version=__version__,
name=cls.getname(qsl),
size=cls.getsize(qsl),
urls=cls.geturls(qsl),
metaurls=cls.getmetaurls(qsl),
hashes=cls.gethashes(qsl))
if __name__ == "__main__":
import sys
app = App()
qsli = [s.lstrip("-").split("=", 1) for s in sys.argv[1:]]
sys.stdout.write(app.makelink(qsli))