mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-08-16 01:47:29 +02:00
Compare commits
11 Commits
d435442b39
...
9a4dca8054
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9a4dca8054 | ||
![]() |
dcd95d3a77 | ||
![]() |
5b2b38ab37 | ||
![]() |
4898926f20 | ||
![]() |
b1ba8ecb2a | ||
![]() |
9ae7690484 | ||
![]() |
e18332efde | ||
![]() |
0ad61f5791 | ||
![]() |
9bcb21389a | ||
![]() |
f65089af86 | ||
![]() |
07834620f3 |
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@ -15,7 +15,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python-version: "3.12.0-alpha.7"
|
||||
- python-version: "3.13.0-alpha.6"
|
||||
with-opt-deps: false # https://github.com/pyca/cryptography/issues/10806
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
- python-version: "3.12.0"
|
||||
with-opt-deps: true
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@ -67,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Upgrade pip
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --upgrade pip setuptools
|
||||
|
||||
- name: Install optional dependencies
|
||||
if: ${{ matrix.with-opt-deps }}
|
||||
|
@ -15,14 +15,10 @@ Last rule: you shouldn't add a mandatory dependency. Limnoria does not
|
||||
come with any (besides Python), so please try to keep all dependencies
|
||||
optional.
|
||||
|
||||
[Style Guidelines]:https://limnoria.readthedocs.io/en/latest/develop/style.html
|
||||
[Style Guidelines]:https://docs.limnoria.net/develop/style.html
|
||||
|
||||
## Sending patches
|
||||
|
||||
When you send a pull request, **send it to the testing branch**.
|
||||
It will be merged to master when it's considered to be stable enough to be
|
||||
supported.
|
||||
|
||||
Don't fear that you spam Limnoria by sending many pull requests. According
|
||||
to @ProgVal, it's easier for them to accept pull requests than to
|
||||
cherry-pick everything manually.
|
||||
@ -32,6 +28,6 @@ is very appreciated.
|
||||
|
||||
See also [Contributing to Limnoria] at [Limnoria documentation].
|
||||
|
||||
[Contributing to Limnoria]:https://limnoria.readthedocs.io/en/latest/contribute/index.html
|
||||
[Contributing to Limnoria]:https://docs.limnoria.net/contribute/index.html
|
||||
|
||||
[Limnoria documentation]:https://limnoria.readthedocs.io/
|
||||
[Limnoria documentation]:https://docs.limnoria.net/
|
||||
|
@ -39,7 +39,7 @@ class DDGTestCase(PluginTestCase):
|
||||
|
||||
def testSearch(self):
|
||||
self.assertRegexp(
|
||||
'ddg search wikipedia', 'Wikipedia.*? - .*?https?\:\/\/')
|
||||
'ddg search wikipedia', r'Wikipedia.*? - .*?https?\:\/\/')
|
||||
self.assertRegexp(
|
||||
'ddg search en.wikipedia.org',
|
||||
'Wikipedia, the free encyclopedia\x02 - '
|
||||
@ -47,6 +47,6 @@ class DDGTestCase(PluginTestCase):
|
||||
with conf.supybot.plugins.DDG.region.context('fr-fr'):
|
||||
self.assertRegexp(
|
||||
'ddg search wikipedia',
|
||||
'Wikipédia, l\'encyclopédie libre - .*?https?\:\/\/')
|
||||
r'Wikipédia, l\'encyclopédie libre - .*?https?\:\/\/')
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
||||
|
@ -194,7 +194,7 @@ class Connection:
|
||||
if code != 151 or code is None:
|
||||
break
|
||||
|
||||
resultword, resultdb = re.search('^"(.+)" (\S+)', text).groups()
|
||||
resultword, resultdb = re.search(r'^"(.+)" (\S+)', text).groups()
|
||||
defstr = self.get100block()
|
||||
retval.append(Definition(self, self.getdbobj(resultdb),
|
||||
resultword, defstr))
|
||||
|
@ -33,11 +33,11 @@ import datetime
|
||||
|
||||
# Credits for the regexp and function: https://stackoverflow.com/a/2765366/539465
|
||||
_XSD_DURATION_RE = re.compile(
|
||||
"(?P<sign>-?)P"
|
||||
"(?:(?P<years>\d+)Y)?"
|
||||
"(?:(?P<months>\d+)M)?"
|
||||
"(?:(?P<days>\d+)D)?"
|
||||
"(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?"
|
||||
r"(?P<sign>-?)P"
|
||||
r"(?:(?P<years>\d+)Y)?"
|
||||
r"(?:(?P<months>\d+)M)?"
|
||||
r"(?:(?P<days>\d+)D)?"
|
||||
r"(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?"
|
||||
)
|
||||
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
|
||||
import time
|
||||
import socket
|
||||
import telnetlib
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.utils as utils
|
||||
@ -158,14 +157,14 @@ class Internet(callbacks.Plugin):
|
||||
if not status:
|
||||
status = 'unknown'
|
||||
try:
|
||||
t = telnetlib.Telnet('whois.iana.org', 43)
|
||||
sock = socket.create_connection(('whois.iana.org', 43))
|
||||
except socket.error as e:
|
||||
irc.error(str(e))
|
||||
return
|
||||
t.write(b'registrar ')
|
||||
t.write(registrar.split('(')[0].strip().encode('ascii'))
|
||||
t.write(b'\n')
|
||||
s = t.read_all()
|
||||
sock.sendall(b'registrar ')
|
||||
sock.sendall(registrar.split('(')[0].strip().encode('ascii'))
|
||||
sock.sendall(b'\n')
|
||||
s = sock.recv(100000)
|
||||
url = ''
|
||||
for line in s.splitlines():
|
||||
line = line.decode('ascii').strip()
|
||||
|
@ -40,7 +40,6 @@ class InternetTestCase(PluginTestCase):
|
||||
'Host not found.')
|
||||
|
||||
def testWhois(self):
|
||||
self.assertNotError('internet whois ohio-state.edu')
|
||||
self.assertNotError('internet whois microsoft.com')
|
||||
self.assertNotError('internet whois inria.fr')
|
||||
self.assertNotError('internet whois slime.com.au')
|
||||
|
@ -849,7 +849,7 @@ class UnitGroup:
|
||||
|
||||
def updateCurrentUnit(self, text, cursorPos):
|
||||
"Set current unit number"
|
||||
self.currentNum = len(re.findall('[\*/]', text[:cursorPos]))
|
||||
self.currentNum = len(re.findall(r'[\*/]', text[:cursorPos]))
|
||||
|
||||
def currentUnit(self):
|
||||
"Return current unit if its a full match, o/w None"
|
||||
@ -925,7 +925,7 @@ class UnitGroup:
|
||||
def parseGroup(self, text):
|
||||
"Return list of units from text string"
|
||||
unitList = []
|
||||
parts = [part.strip() for part in re.split('([\*/])', text)]
|
||||
parts = [part.strip() for part in re.split(r'([\*/])', text)]
|
||||
numerator = 1
|
||||
while parts:
|
||||
unit = self.parseUnit(parts.pop(0))
|
||||
@ -1180,7 +1180,7 @@ class Unit:
|
||||
self.equiv = unitList[0].strip()
|
||||
if self.equiv[0] == '[': # used only for non-linear units
|
||||
try:
|
||||
self.equiv, self.fromEqn = re.match('\[(.*?)\](.*)', \
|
||||
self.equiv, self.fromEqn = re.match(r'\[(.*?)\](.*)', \
|
||||
self.equiv).groups()
|
||||
if ';' in self.fromEqn:
|
||||
self.fromEqn, self.toEqn = self.fromEqn.split(';', 1)
|
||||
@ -1190,7 +1190,7 @@ class Unit:
|
||||
raise UnitDataError('Bad equation for "%s"' % self.name)
|
||||
else: # split factor and equiv unit for linear
|
||||
parts = self.equiv.split(None, 1)
|
||||
if len(parts) > 1 and re.search('[^\d\.eE\+\-\*/]', parts[0]) \
|
||||
if len(parts) > 1 and re.search(r'[^\d\.eE\+\-\*/]', parts[0]) \
|
||||
== None: # only allowed digits and operators
|
||||
try:
|
||||
self.factor = float(eval(parts[0]))
|
||||
|
@ -342,21 +342,31 @@ class Misc(callbacks.Plugin):
|
||||
Returns the version of the current bot.
|
||||
"""
|
||||
try:
|
||||
newestUrl = 'https://api.github.com/repos/progval/Limnoria/' + \
|
||||
'commits/%s'
|
||||
versions = {}
|
||||
for branch in ('master', 'testing'):
|
||||
data = json.loads(utils.web.getUrl(newestUrl % branch)
|
||||
.decode('utf8'))
|
||||
version = data['commit']['committer']['date']
|
||||
# Strip the last 'Z':
|
||||
version = version.rsplit('T', 1)[0].replace('-', '.')
|
||||
if minisix.PY2 and isinstance(version, unicode):
|
||||
version = version.encode('utf8')
|
||||
versions[branch] = version
|
||||
newest = _('The newest versions available online are %s.') % \
|
||||
', '.join([_('%s (in %s)') % (y,x)
|
||||
for x,y in versions.items()])
|
||||
versions = []
|
||||
|
||||
# fetch from PyPI
|
||||
data = json.loads(utils.web.getUrl(
|
||||
'https://pypi.org/pypi/limnoria/json'
|
||||
).decode('utf8'))
|
||||
release_version = data['info']['version']
|
||||
# zero-left-pad months and days
|
||||
release_version = re.sub(
|
||||
r'\.([0-9])\b', lambda m: '.0' + m.group(1), release_version
|
||||
)
|
||||
|
||||
# fetch from Git
|
||||
data = json.loads(utils.web.getUrl(
|
||||
'https://api.github.com/repos/progval/Limnoria/'
|
||||
'commits/master'
|
||||
).decode('utf8'))
|
||||
git_version = data['commit']['committer']['date']
|
||||
# Strip the last 'Z':
|
||||
git_version = git_version.rsplit('T', 1)[0].replace('-', '.')
|
||||
|
||||
newest = _(
|
||||
'The newest version available online is %(release_version)s, '
|
||||
'or %(git_version)s in Git'
|
||||
) % {'release_version': release_version, 'git_version': git_version}
|
||||
except utils.web.Error as e:
|
||||
self.log.info('Couldn\'t get website version: %s', e)
|
||||
newest = _('I couldn\'t fetch the newest version '
|
||||
|
@ -280,7 +280,7 @@ class RSS(callbacks.Plugin):
|
||||
raise callbacks.Error(s)
|
||||
if url:
|
||||
feed = self.feeds.get(url)
|
||||
if feed and feed.name != feed.url:
|
||||
if feed and feed.name != feed.url and feed.name in self.feed_names:
|
||||
s = format(_('I already have a feed with that URL named %s.'),
|
||||
feed.name)
|
||||
raise callbacks.Error(s)
|
||||
|
@ -84,7 +84,7 @@ def mock_urllib(f):
|
||||
|
||||
url = 'http://www.advogato.org/rss/articles.xml'
|
||||
class RSSTestCase(ChannelPluginTestCase):
|
||||
plugins = ('RSS','Plugin')
|
||||
plugins = ('RSS', 'Plugin')
|
||||
|
||||
timeout = 1
|
||||
|
||||
@ -121,6 +121,27 @@ class RSSTestCase(ChannelPluginTestCase):
|
||||
self.assertEqual(self.irc.getCallback('RSS').feed_names, {})
|
||||
self.assertTrue(self.irc.getCallback('RSS').get_feed('http://xkcd.com/rss.xml'))
|
||||
|
||||
@mock_urllib
|
||||
def testChangeUrl(self, mock):
|
||||
try:
|
||||
self.assertNotError('rss add xkcd http://xkcd.com/rss.xml')
|
||||
self.assertNotError('rss remove xkcd')
|
||||
self.assertNotError('rss add xkcd https://xkcd.com/rss.xml')
|
||||
self.assertRegexp('help xkcd', 'https://')
|
||||
finally:
|
||||
self._feedMsg('rss remove xkcd')
|
||||
|
||||
@mock_urllib
|
||||
def testChangeName(self, mock):
|
||||
try:
|
||||
self.assertNotError('rss add xkcd http://xkcd.com/rss.xml')
|
||||
self.assertNotError('rss remove xkcd')
|
||||
self.assertNotError('rss add xkcd2 http://xkcd.com/rss.xml')
|
||||
self.assertRegexp('help xkcd2', 'http://xkcd.com')
|
||||
finally:
|
||||
self._feedMsg('rss remove xkcd')
|
||||
self._feedMsg('rss remove xkcd2')
|
||||
|
||||
@mock_urllib
|
||||
def testInitialAnnounceNewest(self, mock):
|
||||
mock._data = xkcd_new
|
||||
|
@ -33,7 +33,6 @@ import os
|
||||
import re
|
||||
import pwd
|
||||
import sys
|
||||
import crypt
|
||||
import errno
|
||||
import random
|
||||
import select
|
||||
@ -41,6 +40,12 @@ import struct
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
try:
|
||||
import crypt
|
||||
except ImportError:
|
||||
# Python >= 3.13
|
||||
crypt = None
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.utils as utils
|
||||
from supybot.commands import *
|
||||
@ -119,25 +124,26 @@ class Unix(callbacks.Plugin):
|
||||
irc.reply(format('%i', os.getpid()), private=True)
|
||||
pid = wrap(pid, [('checkCapability', 'owner')])
|
||||
|
||||
_cryptre = re.compile(b'[./0-9A-Za-z]')
|
||||
@internationalizeDocstring
|
||||
def crypt(self, irc, msg, args, password, salt):
|
||||
"""<password> [<salt>]
|
||||
if crypt is not None: # Python < 3.13
|
||||
_cryptre = re.compile(b'[./0-9A-Za-z]')
|
||||
@internationalizeDocstring
|
||||
def crypt(self, irc, msg, args, password, salt):
|
||||
"""<password> [<salt>]
|
||||
|
||||
Returns the resulting of doing a crypt() on <password>. If <salt> is
|
||||
not given, uses a random salt. If running on a glibc2 system,
|
||||
prepending '$1$' to your salt will cause crypt to return an MD5sum
|
||||
based crypt rather than the standard DES based crypt.
|
||||
"""
|
||||
def makeSalt():
|
||||
s = b'\x00'
|
||||
while self._cryptre.sub(b'', s) != b'':
|
||||
s = struct.pack('<h', random.randrange(-(2**15), 2**15))
|
||||
return s
|
||||
if not salt:
|
||||
salt = makeSalt().decode()
|
||||
irc.reply(crypt.crypt(password, salt))
|
||||
crypt = wrap(crypt, ['something', additional('something')])
|
||||
Returns the resulting of doing a crypt() on <password>. If <salt> is
|
||||
not given, uses a random salt. If running on a glibc2 system,
|
||||
prepending '$1$' to your salt will cause crypt to return an MD5sum
|
||||
based crypt rather than the standard DES based crypt.
|
||||
"""
|
||||
def makeSalt():
|
||||
s = b'\x00'
|
||||
while self._cryptre.sub(b'', s) != b'':
|
||||
s = struct.pack('<h', random.randrange(-(2**15), 2**15))
|
||||
return s
|
||||
if not salt:
|
||||
salt = makeSalt().decode()
|
||||
irc.reply(crypt.crypt(password, salt))
|
||||
crypt = wrap(crypt, ['something', additional('something')])
|
||||
|
||||
@internationalizeDocstring
|
||||
def spell(self, irc, msg, args, word):
|
||||
|
@ -31,6 +31,11 @@
|
||||
import os
|
||||
import socket
|
||||
|
||||
try:
|
||||
import crypt
|
||||
except ImportError:
|
||||
crypt = None
|
||||
|
||||
from supybot.test import *
|
||||
|
||||
try:
|
||||
@ -106,8 +111,9 @@ if os.name == 'posix':
|
||||
def testProgstats(self):
|
||||
self.assertNotError('progstats')
|
||||
|
||||
def testCrypt(self):
|
||||
self.assertNotError('crypt jemfinch')
|
||||
if crypt is not None: # Python < 3.13
|
||||
def testCrypt(self):
|
||||
self.assertNotError('crypt jemfinch')
|
||||
|
||||
@skipUnlessFortune
|
||||
def testFortune(self):
|
||||
|
6
setup.py
6
setup.py
@ -49,10 +49,12 @@ except ImportError:
|
||||
install. This package is pretty standard, and often installed alongside
|
||||
Python, but it is missing on your system.
|
||||
Try installing it with your package manager, it is usually called
|
||||
'python3-setuptools'. If that does not work, try installing python3-pip
|
||||
'python3-setuptools'; or with '%s -m pip install setuptools'.
|
||||
If that does not work, try installing python3-pip
|
||||
instead, either with your package manager or by following these
|
||||
instructions: https://pip.pypa.io/en/stable/installation/ (replace
|
||||
'python' with 'python3' in all the commands)""")
|
||||
'python' with 'python3' in all the commands)"""
|
||||
% sys.executable)
|
||||
sys.stderr.write(os.linesep*2)
|
||||
sys.stderr.write(textwrap.fill(s))
|
||||
sys.stderr.write(os.linesep*2)
|
||||
|
@ -419,6 +419,15 @@ def registerNetwork(name, password='', ssl=True, sasl_username='',
|
||||
registry.String('', _("""Determines what user modes the bot will request
|
||||
from the server when it first connects. If empty, defaults to
|
||||
supybot.protocols.irc.umodes""")))
|
||||
registerGlobalValue(network, 'vhost',
|
||||
registry.String('', _("""Determines what vhost the bot will bind to before
|
||||
connecting a server (IRC, HTTP, ...) via IPv4. If empty, defaults to
|
||||
supybot.protocols.irc.vhost""")))
|
||||
registerGlobalValue(network, 'vhostv6',
|
||||
registry.String('', _("""Determines what vhost the bot will bind to before
|
||||
connecting a server (IRC, HTTP, ...) via IPv6. If empty, defaults to
|
||||
supybot.protocols.irc.vhostv6""")))
|
||||
|
||||
sasl = registerGroup(network, 'sasl')
|
||||
registerGlobalValue(sasl, 'username', registry.String(sasl_username,
|
||||
_("""Determines what SASL username will be used on %s. This should
|
||||
|
@ -312,8 +312,10 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||
address,
|
||||
port=self.currentServer.port,
|
||||
socks_proxy=socks_proxy,
|
||||
vhost=conf.supybot.protocols.irc.vhost(),
|
||||
vhostv6=conf.supybot.protocols.irc.vhostv6(),
|
||||
vhost=self.networkGroup.get('vhost')()
|
||||
or conf.supybot.protocols.irc.vhost(),
|
||||
vhostv6=self.networkGroup.get('vhostv6')()
|
||||
or conf.supybot.protocols.irc.vhostv6(),
|
||||
)
|
||||
except socket.error as e:
|
||||
drivers.log.connectError(self.currentServer, e)
|
||||
|
@ -1,5 +1,5 @@
|
||||
###
|
||||
# Copyright (c) 2011-2021, Valentin Lorentz
|
||||
# Copyright (c) 2011-2024, Valentin Lorentz
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -32,8 +32,8 @@ An embedded and centralized HTTP server for Supybot's plugins.
|
||||
"""
|
||||
|
||||
import os
|
||||
import cgi
|
||||
import socket
|
||||
import urllib.parse
|
||||
from threading import Thread
|
||||
|
||||
import supybot.log as log
|
||||
@ -164,6 +164,114 @@ def get_template(filename):
|
||||
with open(path + '.example', 'r') as fd:
|
||||
return fd.read()
|
||||
|
||||
class HttpHeader:
|
||||
__slots__ = ('name', 'value')
|
||||
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
"""Return printable representation."""
|
||||
return "HttpHeader(%r, %r)" % (self.name, self.value)
|
||||
|
||||
class HttpHeaders:
|
||||
"""Copy of `cgi.FieldStorage
|
||||
<https://github.com/python/cpython/blob/v3.12.3/Lib/cgi.py#L512-L594>`
|
||||
before it was removed from the stdlib.
|
||||
"""
|
||||
__slots__ = ('list',)
|
||||
def __init__(self, headers):
|
||||
self.list = headers
|
||||
|
||||
def __repr__(self):
|
||||
return 'HttpHeaders(%r)' % self.list
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.keys())
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name != 'value':
|
||||
raise AttributeError(name)
|
||||
if self.file:
|
||||
self.file.seek(0)
|
||||
value = self.file.read()
|
||||
self.file.seek(0)
|
||||
elif self.list is not None:
|
||||
value = self.list
|
||||
else:
|
||||
value = None
|
||||
return value
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Dictionary style indexing."""
|
||||
if self.list is None:
|
||||
raise TypeError("not indexable")
|
||||
found = []
|
||||
for item in self.list:
|
||||
if item.name == key: found.append(item)
|
||||
if not found:
|
||||
raise KeyError(key)
|
||||
if len(found) == 1:
|
||||
return found[0]
|
||||
else:
|
||||
return found
|
||||
|
||||
def getvalue(self, key, default=None):
|
||||
"""Dictionary style get() method, including 'value' lookup."""
|
||||
if key in self:
|
||||
value = self[key]
|
||||
if isinstance(value, list):
|
||||
return [x.value for x in value]
|
||||
else:
|
||||
return value.value
|
||||
else:
|
||||
return default
|
||||
|
||||
def getfirst(self, key, default=None):
|
||||
""" Return the first value received."""
|
||||
if key in self:
|
||||
value = self[key]
|
||||
if isinstance(value, list):
|
||||
return value[0].value
|
||||
else:
|
||||
return value.value
|
||||
else:
|
||||
return default
|
||||
|
||||
def getlist(self, key):
|
||||
""" Return list of received values."""
|
||||
if key in self:
|
||||
value = self[key]
|
||||
if isinstance(value, list):
|
||||
return [x.value for x in value]
|
||||
else:
|
||||
return [value.value]
|
||||
else:
|
||||
return []
|
||||
|
||||
def keys(self):
|
||||
"""Dictionary style keys() method."""
|
||||
if self.list is None:
|
||||
raise TypeError("not indexable")
|
||||
return list(set(item.name for item in self.list))
|
||||
|
||||
def __contains__(self, key):
|
||||
"""Dictionary style __contains__ method."""
|
||||
if self.list is None:
|
||||
raise TypeError("not indexable")
|
||||
return any(item.name == key for item in self.list)
|
||||
|
||||
def __len__(self):
|
||||
"""Dictionary style len(x) support."""
|
||||
return len(self.keys())
|
||||
|
||||
def __bool__(self):
|
||||
if self.list is None:
|
||||
raise TypeError("Cannot be converted to bool.")
|
||||
return bool(self.list)
|
||||
|
||||
|
||||
class SupyHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
def do_X(self, callbackMethod, *args, **kwargs):
|
||||
if self.path == '/':
|
||||
@ -199,12 +307,11 @@ class SupyHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
if 'Content-Type' not in self.headers:
|
||||
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
if self.headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
||||
form = cgi.FieldStorage(
|
||||
fp=self.rfile,
|
||||
headers=self.headers,
|
||||
environ={'REQUEST_METHOD':'POST',
|
||||
'CONTENT_TYPE':self.headers['Content-Type'],
|
||||
})
|
||||
length = min(100000, int(self.headers.get('Content-Length', '100000')))
|
||||
qs = self.rfile.read(length).decode()
|
||||
form = HttpHeaders([
|
||||
HttpHeader(k, v) for (k, v) in urllib.parse.parse_qsl(qs)
|
||||
])
|
||||
else:
|
||||
content_length = int(self.headers.get('Content-Length', '0'))
|
||||
form = self.rfile.read(content_length)
|
||||
|
Loading…
x
Reference in New Issue
Block a user